├── .eslintrc.js ├── .github └── FUNDING.yml ├── .gitignore ├── .husky ├── _ │ └── husky.sh ├── commit-msg └── pre-commit ├── .vscode └── launch.json ├── LICENSE ├── Quick-start.md ├── README.MD ├── commitlint.config.js ├── package-lock.json ├── package.json ├── src ├── example │ ├── example.html │ ├── mock-202302.js │ ├── tempCodeRunnerFile.js │ └── test.js ├── interview │ ├── README.md │ ├── ali-baoxian.202110.js │ ├── alibaba.202110.js │ ├── kuaishou.202110.js │ ├── pdd-20211109.js │ ├── weimeng.202110.js │ └── xiaohongshu.202110.js ├── js │ ├── LRU │ │ ├── LRU.js │ │ └── LRU缓存.md │ ├── Object.create │ │ └── Object.create.js │ ├── arrayFunction │ │ ├── arrary-function.js │ │ ├── array-reduce.js │ │ ├── array-splice.js │ │ └── 数组方法.md │ ├── assign │ │ ├── Obejct.assgin的模拟实现.md │ │ └── Object-assign.js │ ├── callApplyBind │ │ ├── bind.js │ │ └── call-apply.js │ ├── curry │ │ ├── curry.js │ │ └── 函数科里化.md │ ├── debounceThrottle │ │ ├── debounce-throttle.js │ │ ├── throttleLastRun.js │ │ ├── 加强版防抖+节流.md │ │ ├── 节流函数最后一次调用必须执行.md │ │ └── 防抖节流.md │ ├── deepClone │ │ ├── deepClone.js │ │ └── 深拷贝.md │ ├── extend │ │ ├── es5-es6extend.js │ │ └── es5寄生组合式基础+ES6继承.md │ ├── intanceof │ │ ├── instanceOf实现原理.md │ │ └── intanceof.js │ ├── new │ │ ├── new.js │ │ └── new实现原理.md │ ├── promiseAwaitGenerator │ │ ├── promise-allsettled.js │ │ ├── await.js │ │ ├── generator.js │ │ └── promise.js │ ├── sort │ │ ├── arrary-sort-insertionSort.js │ │ ├── arrary-sort-modifiedBubbleSort.js │ │ ├── arrary-sort-selection.js │ │ └── 排序.md │ └── uniqueArray │ │ ├── uniqueArray1.js │ │ ├── uniqueArray2.js │ │ └── 数组去重.md ├── leetCode │ ├── 132-pattern │ │ └── 132-pattern.js │ ├── 3sum │ │ └── 3sum.js │ ├── README.md │ ├── add-two-numbers-ii │ │ └── add-two-numbers-ii.js │ ├── add-two-numbers │ │ └── add-two-numbers.js │ ├── airplane-seat-assignment-probability │ │ └── airplane-seat-assignment-probability.js │ ├── android-unlock-patterns │ │ └── android-unlock-patterns.js │ ├── assign-cookies │ │ └── assign-cookies.js │ ├── asteroid-collision │ │ └── asteroid-collision.js │ ├── ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof │ │ └── ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof.js │ ├── backspace-string-compare │ │ └── backspace-string-compare.js │ ├── balanced-binary-tree │ │ └── balanced-binary-tree.js │ ├── bao-han-minhan-shu-de-zhan-lcof │ │ └── bao-han-minhan-shu-de-zhan-lcof.js │ ├── baseball-game │ │ └── baseball-game.js │ ├── best-time-to-buy-and-sell-stock-ii │ │ └── best-time-to-buy-and-sell-stock-ii.js │ ├── best-time-to-buy-and-sell-stock │ │ └── best-time-to-buy-and-sell-stock.js │ ├── binary-tree-inorder-traversal │ │ └── binary-tree-inorder-traversal.js │ ├── binary-tree-level-order-traversal │ │ └── binary-tree-level-order-traversal.js │ ├── binary-tree-paths │ │ └── binary-tree-paths.js │ ├── binary-tree-postorder-traversal │ │ └── binary-tree-postorder-traversal.js │ ├── binary-tree-right-side-view │ │ └── binary-tree-right-side-view.js │ ├── binary-tree-tilt │ │ └── binary-tree-tilt.js │ ├── binary-tree-zigzag-level-order-traversal │ │ └── binary-tree-zigzag-level-order-traversal.js │ ├── build-an-array-with-stack-operations │ │ └── build-an-array-with-stack-operations.js │ ├── can-place-flowers │ │ └── can-place-flowers.js │ ├── check-array-formation-through-concatenation │ │ └── check-array-formation-through-concatenation.js │ ├── climbing-stairs │ │ └── climbing-stairs.js │ ├── coin-change │ │ └── coin-change.js │ ├── cong-wei-dao-tou-da-yin-lian-biao-lcof │ │ └── cong-wei-dao-tou-da-yin-lian-biao-lcof.js │ ├── construct-binary-tree-from-preorder-and-inorder-traversal │ │ └── construct-binary-tree-from-preorder-and-inorder-traversal.js │ ├── container-with-most-water │ │ └── container-with-most-water.js │ ├── contiguous-sequence-lcci │ │ └── contiguous-sequence-lcci.js │ ├── convert-sorted-array-to-binary-search-tree │ │ └── convert-sorted-array-to-binary-search-tree.js │ ├── count-the-repetitions │ │ └── count-the-repetitions.js │ ├── course-schedule-ii │ │ └── course-schedule-ii.js │ ├── course-schedule │ │ └── course-schedule.js │ ├── daily-temperatures │ │ └── daily-temperatures.js │ ├── decode-string │ │ └── decode-string.js │ ├── delete-node-in-a-linked-list │ │ └── delete-node-in-a-linked-list.js │ ├── deleteLowerStr │ │ ├── 删除字符串中出现次数最少的字符.md │ │ └── index.js │ ├── diameter-of-binary-tree │ │ └── diameter-of-binary-tree.js │ ├── divisor-game │ │ └── divisor-game.js │ ├── editDiatance │ │ ├── editDiatance.js │ │ └── index.md │ ├── er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof │ │ └── er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof.js │ ├── er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof │ │ └── er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof.js │ ├── fei-bo-na-qi-shu-lie-lcof │ │ └── fei-bo-na-qi-shu-lie-lcof.js │ ├── flatten-binary-tree-to-linked-list │ │ └── flatten-binary-tree-to-linked-list.js │ ├── gas-station │ │ └── gas-station.js │ ├── generate-parentheses │ │ └── generate-parentheses.js │ ├── hanota-lcci │ │ └── hanota-lcci.js │ ├── house-robber-ii │ │ └── house-robber-ii.js │ ├── house-robber-iii │ │ └── house-robber-iii.js │ ├── house-robber │ │ └── house-robber.js │ ├── implement-queue-using-stacks │ │ └── implement-queue-using-stacks.js │ ├── intersection-of-two-arrays-ii │ │ └── intersection-of-two-arrays-ii.js │ ├── intersection-of-two-arrays │ │ └── intersection-of-two-arrays.js │ ├── intersection-of-two-linked-lists │ │ └── intersection-of-two-linked-lists.js │ ├── invert-binary-tree │ │ └── invert-binary-tree.js │ ├── is-subsequence │ │ └── is-subsequence.js │ ├── island-perimeter │ │ └── island-perimeter.js │ ├── ji-qi-ren-de-yun-dong-fan-wei-lcof │ │ └── ji-qi-ren-de-yun-dong-fan-wei-lcof.js │ ├── jump-game-ii │ │ └── jump-game-ii.js │ ├── jump-game │ │ └── jump-game.js │ ├── kth-largest-element-in-an-array │ │ └── kth-largest-element-in-an-array.js │ ├── largest-number │ │ └── largest-number.js │ ├── largest-perimeter-triangle │ │ └── largest-perimeter-triangle.js │ ├── last-stone-weight-ii │ │ └── last-stone-weight-ii.js │ ├── last-stone-weight │ │ └── last-stone-weight.js │ ├── lemonade-change │ │ └── lemonade-change.js │ ├── lexicographical-numbers │ │ └── lexicographical-numbers.js │ ├── lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof │ │ └── lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof.js │ ├── lian-xu-zi-shu-zu-de-zui-da-he-lcof │ │ └── lian-xu-zi-shu-zu-de-zui-da-he-lcof.js │ ├── linked-list-cycle │ │ └── linked-list-cycle.js │ ├── longest-palindromic-substring │ │ └── longest-palindromic-substring.js │ ├── longest-substring-without-repeating-characters │ │ └── longest-substring-without-repeating-characters.js │ ├── lowest-common-ancestor-of-a-binary-tree │ │ └── lowest-common-ancestor-of-a-binary-tree.js │ ├── lru-cache │ │ └── lru-cache.js │ ├── majority-element │ │ └── majority-element.js │ ├── matrix-cells-in-distance-order │ │ └── matrix-cells-in-distance-order.js │ ├── max-area-of-island │ │ └── max-area-of-island.js │ ├── maximum-depth-of-binary-tree │ │ └── maximum-depth-of-binary-tree.js │ ├── maximum-depth-of-n-ary-tree │ │ └── maximum-depth-of-n-ary-tree.js │ ├── maximum-subarray │ │ └── maximum-subarray.js │ ├── maximum-swap │ │ └── maximum-swap.js │ ├── maximum-width-of-binary-tree │ │ └── maximum-width-of-binary-tree.js │ ├── meeting-rooms-ii │ │ └── meeting-rooms-ii.js │ ├── merge-intervals │ │ └── merge-intervals.js │ ├── merge-sorted-array │ │ └── merge-sorted-array.js │ ├── merge-two-binary-trees │ │ └── merge-two-binary-trees.js │ ├── merge-two-sorted-lists │ │ └── merge-two-sorted-lists.js │ ├── middle-of-the-linked-list │ │ └── middle-of-the-linked-list.js │ ├── min-cost-climbing-stairs │ │ └── min-cost-climbing-stairs.js │ ├── minimum-depth-of-binary-tree │ │ └── minimum-depth-of-binary-tree.js │ ├── minimum-path-sum │ │ └── minimum-path-sum.js │ ├── minimum-remove-to-make-valid-parentheses │ │ └── minimum-remove-to-make-valid-parentheses.js │ ├── minimum-subsequence-in-non-increasing-order │ │ └── minimum-subsequence-in-non-increasing-order.js │ ├── next-greater-element-i │ │ └── next-greater-element-i.js │ ├── next-greater-element-ii │ │ └── next-greater-element-ii.js │ ├── next-permutation │ │ └── next-permutation.js │ ├── number-of-atoms │ │ └── number-of-atoms.js │ ├── number-of-islands │ │ └── number-of-islands.js │ ├── pacific-atlantic-water-flow │ │ └── pacific-atlantic-water-flow.js │ ├── palindrome-linked-list │ │ └── palindrome-linked-list.js │ ├── partition-equal-subset-sum │ │ └── partition-equal-subset-sum.js │ ├── pascals-triangle │ │ └── pascals-triangle.js │ ├── path-sum-ii │ │ └── path-sum-ii.js │ ├── path-sum │ │ └── path-sum.js │ ├── perfect-squares │ │ └── perfect-squares.js │ ├── permutations │ │ └── permutations.js │ ├── qing-wa-tiao-tai-jie-wen-ti-lcof │ │ └── qing-wa-tiao-tai-jie-wen-ti-lcof.js │ ├── queue-reconstruction-by-height │ │ └── queue-reconstruction-by-height.js │ ├── range-sum-query-immutable │ │ └── range-sum-query-immutable.js │ ├── relative-sort-array │ │ └── relative-sort-array.js │ ├── remove-all-adjacent-duplicates-in-string │ │ └── remove-all-adjacent-duplicates-in-string.js │ ├── remove-duplicates-from-sorted-array │ │ └── remove-duplicates-from-sorted-array.js │ ├── remove-duplicates-from-sorted-list │ │ └── remove-duplicates-from-sorted-list.js │ ├── remove-k-digits │ │ └── remove-k-digits.js │ ├── remove-linked-list-elements │ │ └── remove-linked-list-elements.js │ ├── remove-nth-node-from-end-of-list │ │ └── remove-nth-node-from-end-of-list.js │ ├── reorder-list │ │ └── reorder-list.js │ ├── restore-ip-addresses │ │ └── restore-ip-addresses.js │ ├── reverse-linked-list │ │ └── reverse-linked-list.js │ ├── same-tree │ │ └── same-tree.js │ ├── search-in-rotated-sorted-array │ │ └── search-in-rotated-sorted-array.js │ ├── shan-chu-lian-biao-de-jie-dian-lcof │ │ └── shan-chu-lian-biao-de-jie-dian-lcof.js │ ├── shu-de-zi-jie-gou-lcof │ │ └── shu-de-zi-jie-gou-lcof.js │ ├── shu-zu-zhong-zhong-fu-de-shu-zi-lcof │ │ └── shu-zu-zhong-zhong-fu-de-shu-zi-lcof.js │ ├── simplify-path │ │ └── simplify-path.js │ ├── sort-list │ │ └── sort-list.js │ ├── spiral-matrix │ │ └── spiral-matrix.js │ ├── split-a-string-in-balanced-strings │ │ └── split-a-string-in-balanced-strings.js │ ├── subtree-of-another-tree │ │ └── subtree-of-another-tree.js │ ├── swap-nodes-in-pairs │ │ └── swap-nodes-in-pairs.js │ ├── symmetric-tree │ │ └── symmetric-tree.js │ ├── target-sum │ │ └── target-sum.js │ ├── the-masseuse-lcci │ │ └── the-masseuse-lcci.js │ ├── three-steps-problem-lcci │ │ └── three-steps-problem-lcci.js │ ├── transpose-matrix │ │ └── transpose-matrix.js │ ├── two-sum │ │ └── two-sum.js │ ├── unique-binary-search-trees │ │ └── unique-binary-search-trees.js │ ├── valid-anagram │ │ └── valid-anagram.js │ ├── valid-parentheses │ │ └── valid-parentheses.js │ ├── validate-binary-search-tree │ │ └── validate-binary-search-tree.js │ ├── yong-liang-ge-zhan-shi-xian-dui-lie-lcof │ │ └── yong-liang-ge-zhan-shi-xian-dui-lie-lcof.js │ └── zui-xiao-de-kge-shu-lcof │ │ └── zui-xiao-de-kge-shu-lcof.js ├── scene │ ├── arrToTree │ │ ├── dataToTree.js │ │ ├── index.js │ │ └── 数组转树, 以及函数扩展.md │ ├── cacheApi.js │ ├── compose │ │ ├── compose.js │ │ └── 实现compose函数,类似koa洋葱组件.md │ ├── countOfAtoms.js │ ├── dom.js │ ├── event-loop.js │ ├── event-loop2.js │ ├── event.js │ ├── find-target.js │ ├── findAll.js │ ├── flattenObj.js │ ├── getAllTag.js │ ├── multiplication.js │ ├── numAdd.js │ ├── numberToCn │ │ ├── cnToNumber.js │ │ ├── numberToCn.js │ │ └── 数字转汉语输出、汉语转数字输出.md │ ├── promise │ │ └── retry.js │ ├── proto-console.js │ ├── reportInterval │ │ ├── reportInterval.js │ │ └── 每4秒输出一次, 输出3次.md │ ├── symbol-close.js │ ├── task-concurrent.js │ ├── template.js │ ├── time.js │ ├── toThousands.js │ ├── traffic-lights.js │ ├── url-parse.js │ └── vDomToDom │ │ ├── index.html │ │ └── vDomToDom.js └── sceneDesign │ ├── 前端接口防刷.md │ ├── 大数据列表.md │ └── 转盘组件设计.md ├── static ├── run-code.jpg ├── vscode-debugger.jpg └── web-basic-example.gif ├── test.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: ['airbnb-base'], 7 | parserOptions: { 8 | parser: 'babel-eslint', 9 | ecmaVersion: 8, 10 | sourceType: 'module', 11 | }, 12 | globals: { 13 | document: true, 14 | localStorage: true, 15 | chrome: true, 16 | }, 17 | rules: { 18 | semi: ['error', 'never', { beforeStatementContinuationChars: 'never' }], 19 | 'no-console': 'off', 20 | 'no-restricted-syntax': 'off', 21 | 'no-continue': 'off', 22 | 'max-len': 'off', 23 | // 禁止使用 var 24 | 'consistent-return': 'off', 25 | 'no-param-reassign': 'off', 26 | 'no-use-before-define': 'off', 27 | 'import/prefer-default-export': 'off', 28 | 'no-useless-return': 'off', 29 | 'no-extend-native': 'off', 30 | 'no-throw-literal': 'off', 31 | 'no-plusplus': 'off', 32 | 'no-unused-vars': 'off', 33 | 'no-multiple-empty-lines': 'off', 34 | 'guard-for-in': 'off', 35 | 'no-lonely-if': 'off', 36 | 'no-shadow': 'off', 37 | 'prefer-const': 'off', 38 | 'class-methods-use-this': 'off', 39 | 'no-underscore-dangle': 'off', 40 | 'no-loop-func': 'off', 41 | 'arrow-body-style': 'off', 42 | 'no-proto': 'off', 43 | 'no-prototype-builtins': 'off', 44 | 'prefer-promise-reject-errors': 'off', 45 | 'prefer-destructuring': 'off', 46 | 'no-multi-assign': 'off', 47 | 'no-bitwise': 'off', 48 | 'no-var': 'off', 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: https://github.com/OBKoro1/koro1FileHeader/blob/dev/images/pay.jpg?raw=true # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | 5 | # Log files 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | -------------------------------------------------------------------------------- /.husky/_/husky.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$husky_skip_init" ]; then 3 | debug () { 4 | if [ "$HUSKY_DEBUG" = "1" ]; then 5 | echo "husky (debug) - $1" 6 | fi 7 | } 8 | 9 | readonly hook_name="$(basename "$0")" 10 | debug "starting $hook_name..." 11 | 12 | if [ "$HUSKY" = "0" ]; then 13 | debug "HUSKY env variable is set to 0, skipping hook" 14 | exit 0 15 | fi 16 | 17 | if [ -f ~/.huskyrc ]; then 18 | debug "sourcing ~/.huskyrc" 19 | . ~/.huskyrc 20 | fi 21 | 22 | export readonly husky_skip_init=1 23 | sh -e "$0" "$@" 24 | exitCode="$?" 25 | 26 | if [ $exitCode != 0 ]; then 27 | echo "husky - $hook_name hook exited with code $exitCode (error)" 28 | fi 29 | 30 | exit $exitCode 31 | fi 32 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", // 使用node.js 运行 9 | "request": "launch", 10 | "name": "在vscode中调试js代码", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | // 修改引用地址 "src/scene/event-loop.js", 在vscode中调试对应文件代码 15 | // 调试过程中:右侧是代码变量 输出在下方的调试控制台中 16 | "program": "${workspaceFolder}/src/scene/event-loop.js" // 修改引用地址 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2021-present, OBKoro1 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /Quick-start.md: -------------------------------------------------------------------------------- 1 | ## 快速开始 2 | 3 | ### 目录 4 | 5 | ```js 6 | ├── .vscode 7 | │ ├── launch.json // 在vscode中调试js代码 8 | ├── src 9 | │ ├── js // js基础核心 10 | │ └── scene // 大厂实战场景题 包括场景题、算法题 11 | │ ├── interview // 大厂面试组合题 12 | │ └── example // example 用于浏览器打开html 进行调试 13 | ``` 14 | 15 | ### 找到对应的代码文件开始学习 16 | 17 | ## 运行代码 18 | 19 | ### 运行测试代码-VSCode插件-Code Runner 20 | 21 | 安装[Code Runner](https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner)插件,直接右键点击`run code` 即可在编辑器中执行测试代码,查看输出结果。 22 | 23 | ![](https://github.com/OBKoro1/web-basics/blob/main/static/run-code.jpg?raw=true) 24 | 25 | `Code Runner`确实挺好用的,但缺点是无法断点调试。 26 | 27 | ### 断点调试 28 | 29 | ### 如何在Vscode中调试代码 30 | 31 | 1. 修改`.vscode/launch.json`里面的引用地址 32 | 2. 打上断点(截图所示的红点) 33 | 3. 点击右侧的调试选择`在vscode中调试代码`,开始调试对应的js文件代码 34 | 4. 输出在下面调试控制台中,变量在右侧。 35 | 36 | ![](https://github.com/OBKoro1/web-basics/blob/main/static/vscode-debugger.jpg?raw=true) 37 | 38 | ### 浏览器调试:使用html文件引用js代码 39 | 40 | 提供了一个html文件:`/src/example/index.html`,正常引用js文件,在代码中打`debugger`,在浏览器中调试代码。 -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-case': [0], 5 | 'scope-case': [0], 6 | 'type-enum': [ 7 | 2, 8 | 'always', 9 | [ 10 | 'feat', // 增加新功能 11 | 'fix', // 修复bug 12 | 'build', // 构造工具的或者外部依赖的改动,例如webpack,npm 13 | 'chore', // 不修改src或者test的其余修改,例如构建过程或辅助工具的变动 14 | 'ci', // 与CI(持续集成服务)有关的改动 15 | 'docs', // 只改动了文档相关的内容 16 | 'style', // 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号 17 | 'perf', // 提高性能的改动 18 | 'refactor', // 代码重构时使用 19 | 'revert', // 执行git revert打印的message 20 | 'test', // 添加测试或者修改现有测试 21 | ], 22 | ], 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-basics", 3 | "version": "1.0.0", 4 | "description": "大厂前端需要掌握的JS基础能力,大厂场景题、大厂面试真题欢迎提issue和PR来丰富场景题", 5 | "scripts": { 6 | "lint": "eslint --ext .js src --fix", 7 | "prepare": "husky install", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/OBKoro1/web-basics.git" 13 | }, 14 | "husky": { 15 | "hooks": { 16 | "commit-msg": "commitlint -e $GIT_PARAMS" 17 | } 18 | }, 19 | "publisher": "OBKoro1", 20 | "author": { 21 | "name": "OBKoro1", 22 | "email": "obkoro1@foxmail.com", 23 | "url": "http://obkoro1.com/web_accumulate/" 24 | }, 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/OBKoro1/web-basics/issues", 28 | "email": "obkoro1@foxmail.com" 29 | }, 30 | "homepage": "https://github.com/OBKoro1/web-basics#readme", 31 | "devDependencies": { 32 | "@commitlint/cli": "^14.1.0", 33 | "@commitlint/config-conventional": "^14.1.0", 34 | "eslint": "^8.1.0", 35 | "eslint-config-airbnb-base": "^14.2.1", 36 | "eslint-plugin-import": "^2.25.2", 37 | "husky": "^7.0.4", 38 | "lint-staged": "^11.2.6" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/example/example.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 运行测试代码 打debugger 15 | 16 | 17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/example/tempCodeRunnerFile.js: -------------------------------------------------------------------------------- 1 | function selectionSort(arr) { 2 | for (let i = 0; i < arr.length; i++) { 3 | let indexMin = i 4 | for (let j = i + 1; j < arr.length; j++) { 5 | if (arr[indexMin] > arr[j]) { 6 | indexMin = j 7 | } 8 | } 9 | if (indexMin !== i) { 10 | [arr[i], arr[indexMin]] = [arr[indexMin], arr[i]] 11 | } 12 | } 13 | return arr 14 | } 15 | 16 | // 使用 17 | const oldArr = [3, 4, 5, 1, 2, 7, 8] 18 | 19 | selectionSort(oldArr) 20 | console.log('排序结果', oldArr) 21 | -------------------------------------------------------------------------------- /src/example/test.js: -------------------------------------------------------------------------------- 1 | // 本仓库在其他库上引用 web网站、仓库引用 2 | -------------------------------------------------------------------------------- /src/interview/README.md: -------------------------------------------------------------------------------- 1 | # 面试实战题 2 | 3 | 大厂面试真题组合,题目在实战场景代码题、或者js基础模块中有。 4 | 5 | 只是各个公司会根据情况把他们组合起来,设置一些变种。 6 | 7 | 让我们在规定的时间(大多是半个小时)里面做出来。 8 | 9 | ### 这里面的题目在其他模块都有 10 | 11 | 建议在要面试之前,给自己练手一下。 12 | 13 | #### 定时 14 | 15 | 根据题目量,和复杂程度,闹钟定时半个小时,或者一个小时,看在规定的时间看能不能做出来。 16 | 17 | #### 紧张 18 | 19 | 实际上面试的心态会紧张很多,建议同学们也可以紧张一点,尽量的模拟面试的真实场景。 20 | 21 | ### 欢迎提issue和PR 贡献自己遇到的实战场景题 22 | 23 | 建议文件名格式为: 24 | 25 | 大厂.日期.js 26 | 27 | 比如: alibaba.20211020.js 28 | 如果重名,也可以加上数字: alibaba.20211020-1.js、alibaba.20211020-2.js 29 | 30 | 文件格式: 31 | 32 | 规定时间:半个小时 或者一个小时。 大概估量即可。 33 | 34 | 附上题目。 35 | 36 | 提供测试运行代码。 37 | 38 | 然后空几十行,再写上答案,避免同学一上来就看到答案。 39 | 40 | 41 | 比如: 42 | 43 | ```js 44 | // 规定的时间 45 | 46 | 题目 47 | 48 | 测试用例 49 | 50 | 空十几行,避免同学一上来就看到答案。 51 | 52 | // 答案慎看 53 | // 答案慎看 54 | // 答案慎看 55 | // 答案慎看 56 | 57 | 58 | // 答案注释掉 避免影响同学自己写的答案运行 59 | // 答案 60 | 61 | ``` 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/js/LRU/LRU.js: -------------------------------------------------------------------------------- 1 | // LRU算法 2 | 3 | // LRU数组实现 4 | 5 | // 一般解法,维护一个数组,数组元素为key-value键值对对象,每次获取需要遍历数组 6 | // 工厂函数,具有两个属性 capacity 保存限量,cache 保存缓存 7 | let LRUCache = function (capacity) { 8 | this.capacity = capacity 9 | this.cache = [] 10 | } 11 | 12 | // 实现 get 方法 13 | LRUCache.prototype.get = function (key) { 14 | let index = this.cache.findIndex((item) => item.key === key) 15 | if (index === -1) { 16 | return -1 17 | } 18 | // 删除此元素后插入到数组第一项 19 | let { value } = this.cache[index] 20 | this.cache.splice(index, 1) 21 | this.cache.unshift({ 22 | key, 23 | value, 24 | }) 25 | return value 26 | } 27 | 28 | // 实现 put 方法 29 | LRUCache.prototype.put = function (key, value) { 30 | let index = this.cache.findIndex((item) => item.key === key) 31 | // 想要插入的数据已经存在了,那么直接提升它就可以 32 | if (index > -1) { 33 | this.cache.splice(index, 1) 34 | } else if (this.cache.length >= this.capacity) { 35 | // 若已经到达最大限制,先淘汰一个最久没有使用的 36 | this.cache.pop() 37 | } 38 | this.cache.unshift({ key, value }) 39 | } 40 | 41 | 42 | // LRU map实现 43 | 44 | 45 | // 时间复杂度 O(1),因为 Map 既能保持键值对,还能记住插入顺序。 46 | let LRUCache2 = function (capacity) { 47 | this.cache = new Map() 48 | this.capacity = capacity 49 | } 50 | 51 | LRUCache2.prototype.get = function (key) { 52 | if (this.cache.has(key)) { 53 | // 存在即更新 54 | let temp = this.cache.get(key) 55 | this.cache.delete(key) 56 | this.cache.set(key, temp) 57 | return temp 58 | } 59 | return -1 60 | } 61 | 62 | LRUCache2.prototype.put = function (key, value) { 63 | if (this.cache.has(key)) { 64 | // 存在即更新(删除后加入) 65 | this.cache.delete(key) 66 | } else if (this.cache.size >= this.capacity) { 67 | // 不存在即加入 68 | // 缓存超过最大值,则移除最近没有使用的 69 | // new Map().keys() 返回一个新的 Iterator 对象 70 | this.cache.delete(this.cache.keys().next().value) 71 | } 72 | this.cache.set(key, value) 73 | } 74 | -------------------------------------------------------------------------------- /src/js/LRU/LRU缓存.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/LRU/LRU缓存.md -------------------------------------------------------------------------------- /src/js/Object.create/Object.create.js: -------------------------------------------------------------------------------- 1 | // 将传入的对象作为新对象原型 2 | function myCreate(obj) { 3 | function F() {} 4 | F.prototype = obj 5 | return new F() 6 | } 7 | const obj1 = { p: 1 } 8 | const obj2 = Object.create(obj1) 9 | obj1.p = 2 10 | console.log('obj', obj1, obj2) 11 | -------------------------------------------------------------------------------- /src/js/arrayFunction/array-reduce.js: -------------------------------------------------------------------------------- 1 | // reduce的实现原理 2 | Array.prototype.reduce1 = function (cb, prev) { 3 | for (let i = 0; i < this.length; i++) { 4 | if (prev === undefined) { 5 | // 一开始没传值 使用第一个元素当prev 将下一个元素当成curr 6 | prev = cb(this[i], this[i + 1], i + 1, this) 7 | i++ // 增加指针 8 | } else { 9 | prev = cb(prev, this[i], i, this) // 返回值储存在prev 在下一次循环中传进去 10 | } 11 | } 12 | return prev // 返回结果 13 | } 14 | 15 | // 用法 16 | const res = [1, 2, 3, 4, 5].reduce1((prev, curr, index, arr) => prev + curr) 17 | 18 | // 多维数组展开 19 | const flatFnn = (arr) => arr.reduce( 20 | (prev, curr) => (Array.isArray(curr) ? prev.concat(flatFnn(curr)) : prev.concat(curr)), [], 21 | ) 22 | 23 | let oldArr = [1, [2, [3, [4, [5]]]]] 24 | let newArr = flatFnn(oldArr) 25 | console.log('newArr', newArr) 26 | 27 | 28 | 29 | 30 | // 从前包裹后面的函数执行 使用闭包缓存函数执行 31 | const compose = (...fns) => { 32 | // 返回一个 前面的函数包裹后面的函数执行 33 | return fns.reduce((a, b) => { 34 | // 返回一个函数 存储参数 35 | return (...args) => a(b(...args)) 36 | }) 37 | } 38 | 39 | 40 | 41 | 42 | 43 | 44 | const fn1 = function (a, b) { 45 | return a + b 46 | } 47 | 48 | const fn2 = function (str) { 49 | return `${str}第二个函数处理` 50 | } 51 | 52 | const fn3 = function (str) { 53 | return `${str}第三个函数处理` 54 | } 55 | const final = compose(fn3, fn2, fn1) // 接受函数 从后往前冲 56 | const res2 = final('a', 'b') // 依次执行 返回结果 57 | console.log('res', res2) // ab第二个函数处理第三个函数处理 58 | -------------------------------------------------------------------------------- /src/js/arrayFunction/array-splice.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-22 13:41:03 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2022-01-12 15:59:23 6 | * FilePath : /js-base/src/js/array-splice.js 7 | * description : 实现数组的splice 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | Array.prototype._splice = function (start, deleteCount, ...addList) { 13 | if (start < 0) { 14 | if (Math.abs(start) > this.length) { 15 | start = 0 16 | } else { 17 | start += this.length 18 | } 19 | } 20 | 21 | if (typeof deleteCount === 'undefined') { 22 | deleteCount = this.length - start 23 | } 24 | // 删除 25 | const removeList = this.slice(start, start + deleteCount) 26 | const right = this.slice(start + deleteCount) 27 | // 添加 28 | let addIndex = start 29 | addList.concat(right).forEach((item) => { 30 | this[addIndex] = item 31 | addIndex++ 32 | }) 33 | this.length = addIndex 34 | 35 | return removeList 36 | } 37 | -------------------------------------------------------------------------------- /src/js/arrayFunction/数组方法.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/arrayFunction/数组方法.md -------------------------------------------------------------------------------- /src/js/assign/Obejct.assgin的模拟实现.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/assign/Obejct.assgin的模拟实现.md -------------------------------------------------------------------------------- /src/js/assign/Object-assign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * description: Object.assign的模拟实现 3 | * param target [type] 合并的源对象 4 | * param mergeObjArr [array] 要合并的对象的数组 5 | * return [Object] 合并后的对象 6 | */ 7 | Object.myAssign = function (target, ...mergeObjArr) { 8 | target = Object(target) // 普通类型包装成对象 比如字符串 数字等 9 | for (let i = 0; i < mergeObjArr.length; i++) { 10 | // 过滤掉要合并的对象为null和undefined的情况 11 | if (mergeObjArr[i] !== null || mergeObjArr[i] !== undefined) { 12 | // 遍历要合并对象的属性 13 | for (let key in mergeObjArr[i]) { 14 | // in运算符会查找原型对象上的可枚举属性,所以需要通过Object.prototype.hasOwnProperty方法过滤掉对象原型对象上的属性 15 | if (mergeObjArr[i].hasOwnProperty(key)) { 16 | target[key] = mergeObjArr[i][key] 17 | } 18 | } 19 | } 20 | } 21 | return target 22 | } 23 | 24 | // 示例代码 25 | const proto = { p: 'proto' } 26 | const obj1 = { a: 'aa' } 27 | const obj2 = { b: 'bb' } 28 | // 以proto对象为新对象的__proto__ 29 | const obj3 = Object.create(proto, { 30 | c: { 31 | value: 'cc', 32 | enumerable: true, 33 | }, 34 | }) 35 | console.log(obj3) // {c: 'cc'} 36 | // 输出obj3的构造函数的原型对象 37 | console.log(obj3.__proto__) // {p: 'proto'} 38 | 39 | 40 | 41 | // 说明不会合并原型链(__proto__) 上面的属性 42 | const t1 = Object.myAssign({}, obj1, obj2) 43 | console.log(t1) // {a: "aa", b: "bb"} 44 | // 过滤合并对象为null、undefined的情况 45 | const t2 = Object.myAssign({}, obj1, null, obj2, undefined) 46 | console.log(t2) // {a: "aa", b: "bb"} 47 | // 合并属性 48 | const t3 = Object.myAssign({}, obj1, obj2, obj3) 49 | console.log(t3) // {a: "aa", b: "bb", c: "cc"} 50 | -------------------------------------------------------------------------------- /src/js/callApplyBind/bind.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-08-05 23:53:37 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-04 17:15:51 6 | * FilePath : /js-base/src/js/bind.js 7 | * description : 实现bind函数 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 思路 13 | // 拷贝源函数: 14 | // 通过变量储存源函数 15 | // 使用Object.create复制源函数的prototype给fToBind 16 | // 返回拷贝的函数 17 | // 调用拷贝的函数: 18 | // new调用判断:通过instanceof判断函数是否通过new调用,来决定绑定的context 19 | // 绑定this+传递参数 20 | // 返回源函数的执行结果 21 | 22 | Function.prototype.myBind = function (target, ...params) { 23 | const thisFn = this // 存储源函数以及上方的params(函数参数) 24 | // 对返回的函数 secondParams 二次传参 25 | const fToBind = function (...secondParams) { 26 | // 确定context 上下文 27 | const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用 28 | const context = isNew ? this : Object(target) // new调用就绑定到this上,否则就绑定到传入的target上 29 | // 绑定this的指向并传递参数,返回执行结果 30 | return thisFn.call(context, ...params, ...secondParams) 31 | } 32 | // 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数 33 | if (thisFn.prototype) { 34 | fToBind.prototype = Object.create(thisFn.prototype) 35 | } 36 | return fToBind // 返回拷贝的函数 37 | } 38 | -------------------------------------------------------------------------------- /src/js/callApplyBind/call-apply.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* 3 | * Author : OBKoro1 4 | * Date : 2021-07-29 23:54:01 5 | * LastEditors : OBKoro1 6 | * LastEditTime : 2023-02-08 12:31:43 7 | * FilePath : /web-basics/src/js/callApplyBind/call-apply.js 8 | * description : 实现call、apply方法 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | 13 | Function.prototype.myCall = function (context, ...paramsArr) { 14 | // 确定this执行 15 | if (context === null || context === undefined) { 16 | // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window) 17 | context = window 18 | } else { 19 | context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象 20 | } 21 | // 临时储存函数 22 | const specialPrototype = Symbol('特殊属性Symbol') 23 | context[specialPrototype] = this 24 | const result = context[specialPrototype](...paramsArr) // 通过隐式绑定执行函数并传递参数 绑定this 25 | delete context[specialPrototype] // 删除上下文对象的属性 26 | return result // 返回函数执行结果 27 | } 28 | 29 | // 只改变传参 30 | Function.prototype.myApply = function (context, params) { 31 | // 确定this执行 32 | if (context === null || context === undefined) { 33 | // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window) 34 | context = window 35 | } else { 36 | context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象 37 | } 38 | // 校验参数 39 | const paramsArr = Array.isArray(params) ? params : [params] 40 | // 临时储存函数 41 | const specialPrototype = Symbol('特殊属性Symbol') 42 | context[specialPrototype] = this 43 | const result = context[specialPrototype](...paramsArr) // 通过隐式绑定执行函数并传递参数 绑定this 44 | delete context[specialPrototype] // 删除上下文对象的属性 45 | return result // 返回函数执行结果 46 | } 47 | -------------------------------------------------------------------------------- /src/js/curry/curry.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-07-30 15:03:35 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2023-02-08 13:09:34 6 | * FilePath : /web-basics/src/js/curry/curry.js 7 | * description : 函数柯里化 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 思路: 比较函数参数,不足返回函数等待接收参数,够了就执行 13 | function curry(fn, ...args) { 14 | // 比较参数数量 15 | if (args.length < fn.length) { 16 | // 返回函数 等待接收参数 17 | return function (...args2) { 18 | return curry(fn, ...args, ...args2) 19 | } 20 | } 21 | // 函数参数够了 执行该函数返回结果 22 | return fn.apply(this, args) 23 | } 24 | 25 | // 使用 26 | function sum(a, b, c, d) { 27 | return a + b + c + d 28 | } 29 | const curriedSum = curry(sum, 1) // 保存函数和参数 30 | const res = curriedSum(2) // 保存第2个参数与sum函数 31 | const res2 = res(3) // 保存第3个参数 32 | // 传进第四个参数 参数足够了 执行函数 返回执行结果 不能再传递参数了 33 | const res3 = res2(4) 34 | console.log('res', curriedSum, res, res2, res3) 35 | -------------------------------------------------------------------------------- /src/js/curry/函数科里化.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/curry/函数科里化.md -------------------------------------------------------------------------------- /src/js/debounceThrottle/throttleLastRun.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: 节流函数的最后一次触发必须调用 3 | * @param {Function} fn 要执行的函数 4 | * @param {Number} gapTime 单位时间 5 | * @param {*} ...arr 传递给fn的参数 6 | */ 7 | function throttleLastRun(fn, gapTime, ...arr) { 8 | let last = 0 // 上次执行时间 第一次马上执行 9 | let timeout 10 | return function (...params2) { 11 | let nowTime = Date.now() // 当前时间 12 | // 当前时间-上次执行的时间是否超过间隔时间 就执行回调 13 | let params = [...arr, ...params2] 14 | if (nowTime - last > gapTime) { 15 | clearTimeout(timeout) // 清除最后一次 16 | fn.apply(this, params) // ...arr为fn的参数 17 | last = nowTime // 重置上次执行时间为当前时间 方便下次执行 18 | } else { 19 | clearTimeout(timeout) // 清除上一个最后一次,改为这次为最后一次 20 | // 最后一次必须执行 在下一次执行之前未被执行 则执行最后一次 21 | timeout = setTimeout(() => { 22 | timeout = null 23 | fn.apply(this, params) 24 | }, gapTime) 25 | } 26 | } 27 | } 28 | 29 | // 要防抖的函数 30 | let actionFn = function (a, b) { 31 | console.log('回调', a, b) 32 | } 33 | setInterval(throttleLastRun(actionFn, 1000, 'actionFn参数1', '参数2'), 500) 34 | -------------------------------------------------------------------------------- /src/js/debounceThrottle/加强版防抖+节流.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/debounceThrottle/加强版防抖+节流.md -------------------------------------------------------------------------------- /src/js/debounceThrottle/节流函数最后一次调用必须执行.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/debounceThrottle/节流函数最后一次调用必须执行.md -------------------------------------------------------------------------------- /src/js/debounceThrottle/防抖节流.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/debounceThrottle/防抖节流.md -------------------------------------------------------------------------------- /src/js/deepClone/deepClone.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-prototype-builtins */ 2 | /* 3 | * Author : OBKoro1 4 | * Date : 2021-08-06 01:07:58 5 | * LastEditors : OBKoro1 6 | * LastEditTime : 2021-11-04 16:18:04 7 | * FilePath : /js-base/src/js/deepClone.js 8 | * description : 深拷贝 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | // 深拷贝 13 | // 1. 正则、时间类型处理 14 | // 2. 函数等正常值 返回 15 | // 3. 解决循环引用的问题 16 | 17 | function deepClone(obj, hash = new WeakMap()) { 18 | if (obj == null) return obj 19 | if (obj instanceof RegExp) return new RegExp(obj) // 处理正则类型数据 20 | if (obj instanceof Date) return new Date(obj) // 处理时间类型数据 21 | if (typeof obj !== 'object') return obj // 返回函数等正常值 22 | if (hash.has(obj)) return hash.get(obj) // 查询循环引用 23 | const copy = new obj.constructor() // 根据constructor实例化数组、对象 24 | hash.set(obj, copy) // 设置hash值 用于查询循环引用 25 | for (const key in obj) { 26 | // 循环对象属性 原型链的值 不拷贝 27 | if (obj.hasOwnProperty(key)) { 28 | // 循环递归拷贝 29 | copy[key] = deepClone(obj[key], hash) 30 | } 31 | } 32 | return copy 33 | } 34 | -------------------------------------------------------------------------------- /src/js/deepClone/深拷贝.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/deepClone/深拷贝.md -------------------------------------------------------------------------------- /src/js/extend/es5寄生组合式基础+ES6继承.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/extend/es5寄生组合式基础+ES6继承.md -------------------------------------------------------------------------------- /src/js/intanceof/instanceOf实现原理.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/intanceof/instanceOf实现原理.md -------------------------------------------------------------------------------- /src/js/intanceof/intanceof.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-proto */ 2 | /* 3 | * Author : OBKoro1 4 | * Date : 2021-07-30 15:02:10 5 | * LastEditors : OBKoro1 1677593011@qq.com 6 | * LastEditTime : 2024-04-21 14:29:06 7 | * FilePath : /web-basics/src/js/intanceof/intanceof.js 8 | * description : instanceOf实现原理 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | // 作用:一个对象是否在另一个对象的原型链上 13 | 14 | // 思路:左边变量的原型链上 有 右边变量的原型, 说明左边对象是继承右边对象的。 15 | function instanceOf(left, right) { 16 | let leftValue = left.__proto__ 17 | const rightValue = right.prototype 18 | while (true) { 19 | if (leftValue === null) { 20 | return false // 左边变量的原型链上没找到 21 | } 22 | if (leftValue === rightValue) { 23 | return true // 右边变量的原型在左边变量的原型链上 24 | } 25 | leftValue = leftValue.__proto__ // 找下层原型 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/js/new/new.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-underscore-dangle */ 2 | /* 3 | * Author : OBKoro1 4 | * Date : 2021-07-30 15:00:30 5 | * LastEditors : OBKoro1 1677593011@qq.com 6 | * LastEditTime : 2024-04-22 11:14:14 7 | * FilePath : /web-basics/src/js/new/new.js 8 | * description : new实现原理 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | // 思路:执行函数,挂载原型、判断返回值 13 | function myNew(fn, ...params) { 14 | // 第一个参数为要new的构造函数 其他的为该构造函数的参数 15 | // 挂载原型 执行结果 16 | const target = {} // 挂载原型的对象 17 | target._proto_ = fn.prototype // 原型连接,target是fn的实例 18 | const res = fn.apply(target, params) // 执行函数 将this指向构造函数的实例 19 | // 判断返回值 20 | const type = typeof res // 结果的类型 21 | if (res && (type === 'object' || type === 'function')) { 22 | return res // 构造函数返回其他对象、或者函数 就返回res 23 | } 24 | return target // 否则就返回函数的实例 25 | } 26 | -------------------------------------------------------------------------------- /src/js/new/new实现原理.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/new/new实现原理.md -------------------------------------------------------------------------------- /src/js/promiseAwaitGenerator/ promise-allsettled.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-10-27 17:43:48 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-10-27 17:43:50 6 | * FilePath : /js-base/src/scene/ promise-allsettled.js 7 | * description : promise.allsettled的polify 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 小红书二面 13 | // 解释:数组内都是异步任务,返回所有异步任务的结果 无论是否成功 14 | 15 | // 编译数组,执行结果的方式 16 | Promise.allSettled = function (promises) { 17 | return new Promise((resolve, reject) => { 18 | let total = 0 19 | const resArr = [] 20 | // 循环promise 21 | promises.forEach((item, index) => { 22 | // 执行promise 23 | Promise.resolve(item).then((res) => { 24 | resArr[index] = { 25 | status: 'fulfilled', 26 | value: res, 27 | } 28 | // 记录完成数量 数量达到则返回结果 29 | total++ 30 | if (total === promises.length) { 31 | resolve(resArr) 32 | } 33 | }).catch((err) => { 34 | resArr[index] = { 35 | status: 'rejected', 36 | value: err, 37 | } 38 | total++ 39 | if (total === promises.length) { 40 | resolve(resArr) 41 | } 42 | }) 43 | }) 44 | }) 45 | } 46 | 47 | // 重写数组 48 | Promise.allSettled = function (promises) { 49 | // 重写每个promise 将结果返回到数组元素中 使其都能成功 50 | const mappedPromises = promises.map((p) => p 51 | .then((value) => ({ 52 | // 直接返回结果 53 | status: 'fulfilled', 54 | value, 55 | })) 56 | .catch((reason) => ({ 57 | // 直接返回结果 58 | status: 'rejected', 59 | value: reason, 60 | }))) 61 | return Promise.all(mappedPromises) 62 | } 63 | -------------------------------------------------------------------------------- /src/js/sort/arrary-sort-insertionSort.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-08-06 00:28:43 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2023-02-09 14:26:33 6 | * FilePath : /web-basics/src/js/sort/arrary-sort-insertionSort.js 7 | * description : 插入排序 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | // 在我看来这是基础几个排序中最好用的一个 12 | // 因为你排列的时候 会从前往后排 13 | // 是从前往后排的 前面的都是你已经排好了 14 | // 不符合条件的都往后移 方便你快速查找一些符合条件的值 15 | 16 | // 思路: 每次排一个元素,新元素往前比较 17 | // 比前一个小的,前一个往后移一位,依次排好所有元素。 18 | function insertionSort(arr) { 19 | for (let i = 1; i < arr.length; i++) { 20 | let j = i // 当前已排序好的位置 让后面的值跟前面的比较 21 | let temp = arr[i] // 要插入的值 22 | // 筛选条件 23 | while (j > 0 && arr[j - 1] > temp) { 24 | arr[j] = arr[j - 1] // 把前面不符合条件的值往后移 25 | j-- // 并且更新索引 26 | } 27 | // 遍历结束前面的值都比插入值大 28 | arr[j] = temp 29 | } 30 | return arr 31 | } 32 | 33 | // 使用 34 | const oldArr = [3, 4, 5, 1, 2, 7, 8] 35 | 36 | insertionSort(oldArr) 37 | console.log('排序结果', oldArr) 38 | -------------------------------------------------------------------------------- /src/js/sort/arrary-sort-modifiedBubbleSort.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-08-06 00:28:45 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2023-02-09 14:33:00 6 | * FilePath : /web-basics/src/js/sort/arrary-sort-modifiedBubbleSort.js 7 | * description : 冒泡排序 8 | * 大的值好像气泡慢慢升至表面 9 | * 思路: 双重遍历,相邻比较,前面的比后面的大就交换位置。 10 | * koroFileheader VSCode插件 11 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 12 | */ 13 | 14 | function modifiedBubbleSort(arr) { 15 | const { length } = arr 16 | for (let i = 0; i < length; i++) { 17 | // 每个元素跟其他元素比较 双重遍历 18 | const num = length - 1 - i // 减去上面已经冒泡 排序好的值 提高性能 19 | for (let j = 0; j < num; j++) { 20 | // 比较不同值,交换位置 21 | if (arr[j] > arr[j + 1]) { 22 | [arr[j + 1], arr[j]] = [arr[j], arr[j + 1]] // 交换位置 23 | } 24 | } 25 | } 26 | return arr 27 | } 28 | // 使用 29 | const oldArr = [3, 4, 5, 1, 2, 7, 8] 30 | 31 | modifiedBubbleSort(oldArr) 32 | console.log('排序结果', oldArr) 33 | -------------------------------------------------------------------------------- /src/js/sort/arrary-sort-selection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-08-06 00:23:28 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2023-02-09 14:46:45 6 | * FilePath : /web-basics/src/js/sort/arrary-sort-selection.js 7 | * description : 选择排序 8 | * 思路: 找到目标值的下标,与当前遍历元素的下标交换。 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | function selectionSort(arr) { 13 | for (let i = 0; i < arr.length; i++) { 14 | let indexMin = i // 当前下标 15 | // j = i i就是已经排序过的 16 | for (let j = i + 1; j < arr.length; j++) { 17 | // 寻找目标值 这里是最小值 也可以是其他判断条件 18 | if (arr[indexMin] > arr[j]) { 19 | indexMin = j 20 | } 21 | } 22 | // 下标不同 与当前遍历元素的下标交换。 23 | if (indexMin !== i) { 24 | [arr[indexMin], arr[i]] = [arr[i], arr[indexMin]] 25 | } 26 | } 27 | } 28 | 29 | // 使用 30 | const oldArr = [3, 4, 5, 1, 2, 7, 8] 31 | 32 | selectionSort(oldArr) 33 | console.log('排序结果', oldArr) 34 | -------------------------------------------------------------------------------- /src/js/sort/排序.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/sort/排序.md -------------------------------------------------------------------------------- /src/js/uniqueArray/uniqueArray1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-13 13:09:24 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2022-01-16 14:46:40 6 | * FilePath : /js-base/src/js/常见场景/数组基本类型去重.js 7 | * description : 数组基本类型去重 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | const a = { test: 1 } 13 | const oldArr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 'NaN', 0, 0, 'a', 'a', {}, {}, a, a] 14 | 15 | // ES6基本类型去重 16 | function unique(arr) { 17 | return Array.from(new Set(arr)) 18 | } 19 | // 基本类型 推荐采用这种方式 简单明了 20 | console.log('es6 set', unique(oldArr)) 21 | // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}] 22 | 23 | // includes 24 | function unique3(arr) { 25 | if (!Array.isArray(arr)) return 26 | const array = [] 27 | for (let i = 0; i < arr.length; i++) { 28 | if (!array.includes(arr[i])) { 29 | array.push(arr[i]) 30 | } 31 | } 32 | return array 33 | } 34 | // NaN识别出来了,对象也没去重,正解 35 | console.log('includes', unique3(oldArr)) 36 | // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}, { test: 1 }] 37 | 38 | // indexOf 39 | function unique2(arr) { 40 | if (!Array.isArray(arr)) return 41 | const array = [] 42 | for (let i = 0; i < arr.length; i++) { 43 | if (array.indexOf(arr[i]) === -1) { 44 | array.push(arr[i]) 45 | } 46 | } 47 | return array 48 | } 49 | 50 | // 缺点:无法识别NaN 51 | console.log('indexOf', unique2(oldArr)) 52 | // // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {}, {}, { test: 1 }] 53 | -------------------------------------------------------------------------------- /src/js/uniqueArray/数组去重.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/js/uniqueArray/数组去重.md -------------------------------------------------------------------------------- /src/leetCode/132-pattern/132-pattern.js: -------------------------------------------------------------------------------- 1 | function find132pattern(nums) { 2 | let stack = [] // 栈 获取第二小值 3 | let mid = -Number.MAX_VALUE // 获取中间值 如果这个值有值 就说明有比它大的值 并且在它前面 4 | // 倒序寻找2 处理位置 5 | for (let i = nums.length - 1; i >= 0; i--) { 6 | if (nums[i] < mid) return true // 如果比最小值大 就说明 有比它小的值 再最大值前面 7 | while (stack.length && stack[stack.length - 1] < nums[i]) { 8 | mid = stack.pop() // 比最大值小 9 | } 10 | stack.push(nums[i]) // 添加当前值到栈中 寻找第二小值 11 | } 12 | return false 13 | } 14 | 15 | // var find132pattern = function (nums) { 16 | // if (nums.length <= 2) return false 17 | // const min = [nums[0]] 18 | // const stack = [] 19 | // for (let i = 1; i < nums.length; i++) { 20 | // min[i] = Math.min(min[i - 1], nums[i]) 21 | // } 22 | // for (let i = nums.length - 1; i > 0; i--) { 23 | // // 栈内排除最小值 获取第一个值 24 | // if (nums[i] > min[i]) { 25 | // // 栈内数据比最小值大 26 | // while (stack.length !== 0 && stack[stack.length - 1] <= min[i]) { 27 | // stack.pop() 28 | // } 29 | // // 栈内数据比当前值小 即满足 l1 min && nums[j] < nums[i]) return true 47 | // } 48 | // min = Math.min(min, nums[i]) // 更新最小值 49 | // } 50 | // return false 51 | // } 52 | -------------------------------------------------------------------------------- /src/leetCode/3sum/3sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[][]} 4 | */ 5 | // 双指针 6 | let threeSum = function (nums) { 7 | let ans = [] 8 | const len = nums.length 9 | if (nums == null || len < 3) return ans 10 | nums.sort((a, b) => a - b) // 排序 11 | for (let i = 0; i < len; i++) { 12 | if (nums[i] > 0) break // 排序后 如果当前数字大于0,则三数之和一定大于0,所以结束循环 13 | if (i > 0 && nums[i] === nums[i - 1]) continue // 当前值重复 查找左右指针也会重复 导致结果重复 跳过 14 | let L = i + 1 // 左指针 15 | let R = len - 1 // 右指针 16 | while (L < R) { // 每个值 都查找所有匹配的值 17 | const sum = nums[i] + nums[L] + nums[R] 18 | if (sum === 0) { 19 | ans.push([nums[i], nums[L], nums[R]]) 20 | // 排序后 相同的值 都会在一起 避免后一个值与当前值相等 21 | while (L < R && nums[L] === nums[L + 1]) L++ // 值重复 查找左右指针也会重复 导致结果重复 去重 22 | while (L < R && nums[R] === nums[R - 1]) R-- // 值重复 查找左右指针也会重复 导致结果重复 去重 23 | L++ 24 | R-- 25 | } else if (sum < 0) L++ // 和小于0 左侧值过小 26 | else if (sum > 0) R-- // 和大于0 右侧值过大 27 | } 28 | } 29 | return ans 30 | } 31 | -------------------------------------------------------------------------------- /src/leetCode/README.md: -------------------------------------------------------------------------------- 1 | # LeetCode 2 | 3 | 按照题型分类 常见题型。 4 | 5 | ```js 6 | console.log(1) 7 | ``` -------------------------------------------------------------------------------- /src/leetCode/add-two-numbers-ii/add-two-numbers-ii.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // 1. 获取两个链表的值 3 | // 2. 将两个链表的值相加 添加到一个栈中 4 | // PS: 注意最后值 8 + 7 进制的情况。 5 | // 3. 循环栈 组成新链表 6 | function addTwoNumbers(l1, l2) { 7 | let ten = 0 8 | let stack1 = [] 9 | let stack2 = [] 10 | // 获取链表的值 11 | while (l1) { 12 | stack1.push(l1.val) 13 | l1 = l1.next 14 | } 15 | while (l2) { 16 | stack2.push(l2.val) 17 | l2 = l2.next 18 | } 19 | // 添加所有值 20 | let res = [] 21 | while (stack1.length || stack2.length) { 22 | const item1 = stack1.length ? stack1.pop() : 0 23 | const item2 = stack2.length ? stack2.pop() : 0 24 | let num = item1 + item2 + ten // 每个数字的值 25 | // 处理10进制 26 | if (num > 9) { 27 | ten = 1 // 进一位 28 | num -= 10 29 | } else { 30 | ten = 0 // 取消10进 31 | } 32 | // 添加当前位值 33 | res.push(num) 34 | } 35 | stack1 = null 36 | stack2 = null 37 | // 数组清空了 但是最后的值又进制了 38 | if (ten === 1) { 39 | res.push(1) 40 | } 41 | // 循环栈 组成新链表 42 | const newL = new ListNode() 43 | let cur = newL 44 | while (res.length) { 45 | let ele = res.pop() 46 | cur.next = new ListNode(ele) 47 | cur = cur.next 48 | } 49 | return newL.next 50 | } 51 | -------------------------------------------------------------------------------- /src/leetCode/add-two-numbers/add-two-numbers.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val) { 5 | * this.val = val; 6 | * this.next = null; 7 | * } 8 | */ 9 | /** 10 | * @param {ListNode} l1 11 | * @param {ListNode} l2 12 | * @return {ListNode} 13 | */ 14 | let addTwoNumbers = function (l1, l2) { 15 | let result = new ListNode(null) 16 | let nextRst = result 17 | // 进位 18 | let params = 0 // 传给下一个层级的值 19 | let val = 0 // 传给当前层级的值 20 | 21 | while (l1 != null || l2 != null) { 22 | // 链表节点值 23 | let x = (l1 != null) ? l1.val : 0 24 | let y = (l2 != null) ? l2.val : 0 25 | // 当前节点值 取余 26 | val = (x + y + params) % 10 27 | // 存储倍数 28 | params = Math.floor((x + y + params) / 10) 29 | // 拼接链表 30 | nextRst.next = new ListNode(val) 31 | // 指针更新 32 | nextRst = nextRst.next 33 | if (l1 != null) l1 = l1.next 34 | if (l2 != null) l2 = l2.next 35 | } 36 | 37 | // 可能一条链表比较长 有剩余 拼接 38 | if (params) { 39 | nextRst.next = new ListNode(params) 40 | } 41 | return result.next 42 | } 43 | -------------------------------------------------------------------------------- /src/leetCode/airplane-seat-assignment-probability/airplane-seat-assignment-probability.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | 6 | let nthPersonGetsNthSeat = function (n) { 7 | return n === 1 ? 1.00000 : 0.50000 8 | } 9 | -------------------------------------------------------------------------------- /src/leetCode/assign-cookies/assign-cookies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} g 3 | * @param {number[]} s 4 | * @return {number} 5 | */ 6 | var findContentChildren = function (g, s) { 7 | // 排序饼干 8 | g.sort((a, b) => a - b) 9 | s.sort((a, b) => a - b) 10 | let gIndex = 0 // 现在需要分配饼干的人 从最低胃口开始分配 11 | for (let i = 0; i < s.length; i++) { 12 | // 饼干可以配分配 13 | if (s[i] >= g[gIndex]) { 14 | // 指针指向下一个胃口大的人 15 | gIndex++ 16 | } 17 | } 18 | return gIndex 19 | } 20 | -------------------------------------------------------------------------------- /src/leetCode/asteroid-collision/asteroid-collision.js: -------------------------------------------------------------------------------- 1 | // 栈 2 | function asteroidCollision(ats) { 3 | const stk = [] 4 | for (const t of ats) { 5 | let ok = true // 默认行星 正值添加,负值添加 6 | // 观察示例规律,隐含条件:正值都在左边,负值都在右边 7 | // 相反方向 栈中为正,新添加为负值 8 | while (ok && stk.length > 0 && stk[stk.length - 1] > 0 && t < 0) { 9 | const a = stk[stk.length - 1] // 栈中行星 10 | const b = Math.abs(t) // 负值转换为正值 用于对比 11 | if (a === b) { 12 | // 销毁并且不添加 13 | stk.pop() 14 | ok = false 15 | } 16 | if (a < b) { 17 | // 小于 栈中行星被销毁 18 | stk.pop() 19 | } 20 | if (a > b) { 21 | // 大于 销毁 不添加行星 22 | ok = false 23 | } 24 | } 25 | // 添加新的行星 26 | if (ok) stk.push(t) 27 | } 28 | return stk 29 | } 30 | 31 | 32 | // /** 33 | // * 栈 34 | // * @param {number[]} asteroids 35 | // * @return {number[]} 36 | // */ 37 | // var asteroidCollision = function (asteroids) { 38 | // let len = asteroids.length 39 | // let stack = [] 40 | // for (let i = 0; i < len; i++) { 41 | // stack.push(asteroids[i]) // 一个一个值添加到栈里面 然后控制栈里面的元素 42 | // // 观察示例规律,隐含条件:正值都在左边,负值都在右边 43 | // // 栈中有两个值才比较 44 | // // 验证最后一个值是负数最后第二个值是正数 它们才会碰撞 45 | // // 如果最后一个值是正数 最后第二个值是负数 它们不会碰撞 46 | // while ( 47 | // stack.length >= 2 48 | // && stack[stack.length - 1] < 0 49 | // && stack[stack.length - 2] > 0 50 | // ) { 51 | // // 获取绝对值 52 | // let last = Math.abs(stack[stack.length - 1]) 53 | // let last2 = Math.abs(stack[stack.length - 2]) 54 | // if (last === last2) { 55 | // stack.splice(stack.length - 2, 2) // 相同 一起销毁 56 | // } else if (last > last2) { 57 | // stack.splice(stack.length - 2, 1) // 最后一个值比较大 删除最后第二个 58 | // } else { 59 | // stack.splice(stack.length - 1, 1) // 最后一个值比较小 删除最后第二个 60 | // } 61 | // } 62 | // } 63 | // return stack 64 | // } 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/leetCode/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 先分别拼接 确认大小 再排序 3 | * @param {number[]} nums 4 | * @return {string} 5 | */ 6 | var minNumber = function (nums) { 7 | nums.sort((a, b) => { 8 | let res1 = `${a}${b}` 9 | let res2 = `${b}${a}` 10 | return res1 - res2 11 | }) 12 | if (nums[0] === '0') return '0' // 前导0 13 | return nums.join('') 14 | } 15 | -------------------------------------------------------------------------------- /src/leetCode/backspace-string-compare/backspace-string-compare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @param {string} t 4 | * @return {boolean} 5 | */ 6 | var backspaceCompare = function (s, t) { 7 | let help = (str) => { 8 | let stack = [] // 栈 9 | for (let i = 0; i < str.length; i++) { 10 | if (str[i] === '#') { 11 | // 遇到#则删除一位 12 | if (stack.length) { 13 | stack.pop() 14 | } 15 | } else { 16 | // 否则进栈 17 | stack.push(str[i]) 18 | } 19 | } 20 | return stack.join('') // 转为数组 21 | } 22 | return help(s) === help(t) 23 | } 24 | -------------------------------------------------------------------------------- /src/leetCode/balanced-binary-tree/balanced-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {boolean} 12 | */ 13 | var isBalanced = function (root) { 14 | // 计算节点高度 15 | const hightFn = (node) => { 16 | if (node === null) return 0 17 | const left = hightFn(node.left) 18 | const right = hightFn(node.right) 19 | const height = Math.abs(left - right) // 左右节点高度 20 | // 如果子节点高度大于1 或者本节点高度大于1 即剪枝 21 | if (right === -1 || left === -1 || height > 1) { 22 | return -1 23 | } 24 | return Math.max(left, right) + 1 // 本节点的高度 25 | } 26 | return hightFn(root) !== -1 27 | } 28 | -------------------------------------------------------------------------------- /src/leetCode/bao-han-minhan-shu-de-zhan-lcof/bao-han-minhan-shu-de-zhan-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * min_stack 贪心:两个栈,有一个栈中 永远是当前元素中的最小值 3 | * 两个栈的元素数量相同 4 | * 添加时,一个正常添加,一个只添加最小值 5 | */ 6 | var MinStack = function () { 7 | this.stack = [] // 正常栈 8 | this.min_stack = [] 9 | } 10 | 11 | /** 12 | * @param {number} x 13 | * @return {void} 14 | */ 15 | // 16 | MinStack.prototype.push = function (x) { 17 | this.stack.push(x) 18 | // 初始化一个最大值 用于初始添加 19 | const miniNum = this.min_stack.length === 0 ? Infinity : this.min_stack[this.min_stack.length - 1] 20 | this.min_stack.push(Math.min(miniNum, x)) 21 | } 22 | 23 | /** 24 | * 两个栈数量相同 直接删除 25 | */ 26 | MinStack.prototype.pop = function () { 27 | this.stack.pop() 28 | // 元素数量相同可以直接删除 29 | this.min_stack.pop() 30 | } 31 | 32 | /** 33 | * 正常栈的最后一位元素 34 | * @return {number} 35 | */ 36 | MinStack.prototype.top = function () { 37 | return this.stack[this.stack.length - 1] 38 | } 39 | 40 | /** 41 | * 最小栈的最小值,取最后一位元素 42 | */ 43 | MinStack.prototype.min = function () { 44 | // 直接返回最后一个即可 45 | return this.min_stack[this.min_stack.length - 1] 46 | } 47 | 48 | /** 49 | * Your MinStack object will be instantiated and called as such: 50 | * var obj = new MinStack() 51 | * obj.push(x) 52 | * obj.pop() 53 | * var param_3 = obj.top() 54 | * var param_4 = obj.min() 55 | */ 56 | -------------------------------------------------------------------------------- /src/leetCode/baseball-game/baseball-game.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} ops 3 | * @return {number} 4 | */ 5 | var calPoints = function (ops) { 6 | let stack = [] // 栈维护得分 7 | for (let i = 0; i < ops.length; i++) { 8 | // 根据规则操作栈 9 | if (ops[i] === 'C') { 10 | stack.pop() 11 | } else if (ops[i] === 'D') { 12 | stack.push(stack[stack.length - 1] * 2) 13 | } else if (ops[i] === '+') { 14 | let value = stack[stack.length - 1] + stack[stack.length - 2] 15 | stack.push(value) 16 | } else { 17 | stack.push(Number(ops[i])) 18 | } 19 | } 20 | // 获取总数 21 | return stack.reduce((total, currt) => { 22 | return total + currt 23 | }, 0) 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/best-time-to-buy-and-sell-stock-ii/best-time-to-buy-and-sell-stock-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 贪心算法 3 | * @param {number[]} prices 4 | * @return {number} 5 | */ 6 | let maxProfit = function (prices) { 7 | let max = 0 // 利润 8 | for (let i = 1; i < prices.length; i++) { 9 | let money = prices[i] - prices[i - 1] // 后一天减去前一天 10 | if (money > 0) max += money 11 | } 12 | return max 13 | } 14 | 15 | // /** 16 | // * 设置一个最大值 17 | // * 当估值一直上升时 不卖 18 | // * 19 | // * @param {number[]} prices 20 | // * @return {number} 21 | // */ 22 | // var maxProfit = function (prices) { 23 | // let max = 0; // 利润 24 | // let by = -1; // 当前持有 25 | // let before = -1; // 26 | // for (let i = 0; i < prices.length; i++) { 27 | // // 买入股票 28 | // if (by === -1) { 29 | // by = prices[i] 30 | // continue 31 | // } 32 | // const getMoney = prices[i] - by // 本次交易的利润 33 | // const beforeMoney = before - by // 之前交易的利润 34 | // if (getMoney < beforeMoney) { 35 | // // 之前卖出利润更大 36 | // if (before !== -1 && beforeMoney) { 37 | // // 之前有买卖操作 将其卖了 更新利润 38 | // max = beforeMoney + max 39 | // before = -1 // 更新之前为0 40 | // by = prices[i] // 买入当前股票 41 | // } else { 42 | // // 之前没有买卖操作 第一次操作 更新买入时间 43 | // before = prices[i] 44 | // } 45 | // } else if (getMoney >= beforeMoney && i === prices.length - 1 && getMoney >= 0) { 46 | // // 本次利润最大 并且是最后一位 47 | // max = getMoney + max 48 | // } else if (before === -1 && getMoney <= 0) { 49 | // // 卖股票不挣钱 50 | // // 新价格更低 更新买入时间 第二天买入 51 | // by = prices[i] 52 | // } else { 53 | // // 之前卖出利润小 更新before 54 | // before = prices[i] 55 | // } 56 | // } 57 | // return max 58 | // }; 59 | -------------------------------------------------------------------------------- /src/leetCode/best-time-to-buy-and-sell-stock/best-time-to-buy-and-sell-stock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态规划 3 | * @param {number[]} prices 4 | * @return {number} 5 | */ 6 | let maxProfit = function (prices) { 7 | let min = prices[0] 8 | const dp = [0] 9 | for (let i = 1; i < prices.length; i++) { 10 | min = Math.min(min, prices[i]) // 更新目前的最小股票 11 | dp[i] = Math.max(dp[i - 1], prices[i] - min) // 之前的最大价值与当天对比,更新最多获得多少钱 12 | } 13 | return dp[prices.length - 1] 14 | } 15 | 16 | // /** 17 | // * 设置一个最大利润, 不断更新最大利润 18 | // * 动态规划 19 | // * @param {number[]} prices 20 | // * @return {number} 21 | // */ 22 | // var maxProfit = function (prices) { 23 | // let max = 0; 24 | // let start = prices[0]; // 股票最小值 25 | // for (let i = 1; i < prices.length; i++) { 26 | // const end = prices[i] // 当天价格 27 | // let count = end - start // 利润 28 | // max = Math.max(count, max) // 利润比较大 则更新利润 29 | // start = Math.min(start, end) // 更新股票最小值 保证最低买入 最低买入 不等于卖出 30 | // } 31 | // return max 32 | // }; 33 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-inorder-traversal/binary-tree-inorder-traversal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {number[]} 11 | */ 12 | var inorderTraversal = function (root) { 13 | let res = [] 14 | dfs(root) 15 | function dfs(node) { 16 | if (node) { 17 | if (node.left) { 18 | // 左 19 | dfs(node.left) // 如果遇到左节点 继续递归 20 | } 21 | // 中 22 | res.push(node.val) // 添加当前节点 23 | if (node.right) { 24 | // 右 25 | dfs(node.right) 26 | } 27 | } 28 | } 29 | return res 30 | } 31 | 32 | // var inorderTraversal = function (root) { 33 | // let res = [] 34 | // if (!root) return [] 35 | // const [white, gray] = ['未访问节点', '已访问节点'] 36 | // const stack = [[root, white]] // stack每个元素都是一个数组 用以记录节点是否访问 37 | // while (stack.length !== 0) { 38 | // const [node, color] = stack.pop() // 取出元素 39 | // if (!node) continue // 添加时没有做空节点判断 40 | // if (color === white) { 41 | // // 先添加的节点后取出来 也就是后添加进Res 42 | // stack.push([node.right, white]) 43 | // stack.push([node, gray]) // 当前节点已访问了 下次遇见直接添加即可 44 | // stack.push([node.left, white]) 45 | // } else { 46 | // res.push(node.val) // 遇到灰色的节点直接添加 47 | // } 48 | // } 49 | // return res 50 | // } 51 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-level-order-traversal/binary-tree-level-order-traversal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {number[][]} 11 | */ 12 | // DFS 13 | var levelOrder = function (root) { 14 | if (!root) return [] 15 | let res = [] 16 | const help = (node, deep) => { 17 | if (!node) return 18 | // 初始化层级 19 | if (!res[deep]) { 20 | res[deep] = [] 21 | } 22 | // 从左到右 添加到对应的层级 后面的放在后面 23 | res[deep].push(node.val) 24 | // 递归套路 25 | deep++ 26 | help(node.left, deep) 27 | help(node.right, deep) 28 | } 29 | help(root, 0) 30 | return res 31 | } 32 | // BFS 33 | // var levelOrder = function (root) { 34 | // if (!root) return [] 35 | // let res = [] 36 | // let stack = [root] 37 | // while (stack.length) { 38 | // let len = stack.length 39 | // let levelList = [] // 层级套路 40 | // // 栈循环 套路 41 | // while (len) { 42 | // // 取出套路 43 | // let node = stack.pop() 44 | // levelList.push(node.val) 45 | // // 添加套路 46 | // if (node.left) stack.unshift(node.left) 47 | // if (node.right) stack.unshift(node.right) 48 | // len-- 49 | // } 50 | // res.push(levelList) 51 | // } 52 | // return res 53 | // }; 54 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-paths/binary-tree-paths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * 深度优先递归 只有叶子节点才添加 没值就return 11 | * @param {TreeNode} root 12 | * @return {string[]} 13 | */ 14 | var binaryTreePaths = function (root) { 15 | let resArr = [] // 结果 16 | const helperFn = (node, res) => { 17 | if (node === null) return // 循环终止 18 | // 添加当前节点到路径中 19 | res += `${node.val}->` 20 | // 叶子节点添加到最终结果中 21 | if (node.left === null && node.right === null) { 22 | res = res.substr(0, res.length - 2) // 去掉最后的-> 23 | resArr.push(res) // 添加该叶子节点 24 | return 25 | } 26 | // 递归左右子节点 27 | helperFn(node.left, res) 28 | helperFn(node.right, res) 29 | } 30 | helperFn(root, '') 31 | return resArr 32 | } 33 | 34 | // 广度优先 35 | // 两个栈 两个指针 每次推出同一个路径和树 36 | // var binaryTreePaths = function (root) { 37 | // let res = [] 38 | // let node_stack = [root] // 节点栈 39 | // let path_stack = [`${root.val}`] // 路径栈 40 | // while (node_stack.length && path_stack.length) { 41 | // let node = node_stack.shift() 42 | // let path = path_stack.shift() 43 | // // 叶子节点 添加进结果 44 | // if (node.left === null && node.right === null) { 45 | // res.push(path) 46 | // } else { 47 | // // 有节点才添加 48 | // if (node.left !== null) { 49 | // node_stack.push(node.left) 50 | // path_stack.push(`${path}->${node.left.val}`) 51 | // } 52 | // if (node.right !== null) { 53 | // node_stack.push(node.right) 54 | // path_stack.push(`${path}->${node.right.val}`) 55 | // } 56 | // } 57 | // } 58 | // return res 59 | // } 60 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-postorder-traversal/binary-tree-postorder-traversal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number[]} 12 | */ 13 | var postorderTraversal = function (root) { 14 | let res = [] 15 | // 后序遍历 左右根 16 | const help = (node) => { 17 | if (!node) return 18 | help(node.left) 19 | help(node.right) 20 | res.push(node.val) 21 | } 22 | help(root) 23 | return res 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-right-side-view/binary-tree-right-side-view.js: -------------------------------------------------------------------------------- 1 | // 广度优先 2 | // 取出每层 最后一个元素 3 | var rightSideView = function (root) { 4 | if (!root) return [] 5 | let queue = [root] // 队列 把树顶加入队列 6 | let arr = [] // 用来存储每层最后个元素值 7 | while (queue.length > 0) { 8 | let len = queue.length // 当前层的广度 9 | while (len) { 10 | let node = queue.shift() // 依次取出当前层队列的元素 从左到右 11 | if (len === 1) arr.push(node.val) // 当是 当前一层的最后一个元素时,把值加入arr 12 | if (node.left) queue.push(node.left) // 先添加左侧的 13 | if (node.right) queue.push(node.right) // 最后添加右侧的 等到最后一个元素时即可添加右侧的值 14 | len-- 15 | } 16 | } 17 | return arr 18 | } 19 | 20 | // 深度优先 21 | // 根据deep深度与当前结果的长度 决定是否添加结果 22 | // 先递归右侧节点 保证右侧节点优先 23 | // var rightSideView = function (root) { 24 | // let res = [] 25 | // function help(node, deep) { 26 | // if (!node) return 27 | // // 结果小于等于deep深度 添加进去 28 | // // res.length在同一层级会变化 它会先添加右侧的节点 因为右侧的先递归 29 | // if (res.length <= deep) { 30 | // res.push(node.val) 31 | // } 32 | // deep++ // 添加深度 33 | // // 先深度递归右侧节点 保证同一层级先添加右侧节点 34 | // help(node.right, deep) 35 | // help(node.left, deep) 36 | // } 37 | // help(root, 0) 38 | // return res 39 | // } 40 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-tilt/binary-tree-tilt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * 没有值 就是0 11 | * 获取左右树的和 12 | * 叠加所有节点的坡度 13 | * @param {TreeNode} root 14 | * @return {number} 15 | */ 16 | var findTilt = function (root) { 17 | let total = 0 18 | const helpFn = (node) => { 19 | if (!node) return 0 20 | let left = helpFn(node.left) // 左节点的值 21 | let right = helpFn(node.right) // 右节点的值 22 | total += Math.abs(left - right) // 计算每个节点坡度 叠加起来就是整个树的节点坡度 23 | return left + right + node.val // 返回该节点值 24 | } 25 | helpFn(root) 26 | return total 27 | } 28 | -------------------------------------------------------------------------------- /src/leetCode/binary-tree-zigzag-level-order-traversal/binary-tree-zigzag-level-order-traversal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {number[][]} 11 | */ 12 | // BFS广度思路: 13 | // x y 左右节点push到stack中 14 | // 1 2 3 4 15 | // 从头拿出来 左边就push 把后面的放到后面, 右边是unshift把后面的插入到前面 16 | // var zigzagLevelOrder = function (root) { 17 | // if (!root) return [] 18 | // let type = 'left' 19 | // let stack = [root] 20 | // let res = [] 21 | // while (stack.length) { 22 | // let len = stack.length 23 | // let levelList = []; // 层级 24 | // while (len) { 25 | // let node = stack.shift() // 从头取出来 26 | // // 左边就push 把后面的放到后面, 右边是unshift把后面的插入到前面 27 | // if (type === 'left') { 28 | // levelList.push(node.val) 29 | // } else { 30 | // levelList.unshift(node.val) 31 | // } 32 | // // 左右节点直接添加即可 33 | // if (node.left) stack.push(node.left) 34 | // if (node.right) stack.push(node.right) 35 | // len-- 36 | // } 37 | // type = type === 'left' ? 'right' : 'left' // 更改方向 38 | // res.push(levelList) // 添加层级 39 | // } 40 | // return res 41 | // }; 42 | 43 | // BFS 深度思路: 44 | // deep深度来判断层级 来判断往前还是往后添加 45 | var zigzagLevelOrder = function (root) { 46 | if (!root) return [] 47 | let res = [] 48 | const BFSHelp = (node, deep) => { 49 | if (!node) return 50 | // 初始化层级 51 | if (!res[deep]) { 52 | res[deep] = [] 53 | } 54 | if (deep % 2 === 0) { 55 | // 双数 往左 从左开始 把后面的放在后面 56 | res[deep].push(node.val) 57 | } else { 58 | // 单数 往右 从左开始 把后面的放在前面 59 | res[deep].unshift(node.val) 60 | } 61 | BFSHelp(node.left, deep + 1) 62 | BFSHelp(node.right, deep + 1) 63 | } 64 | BFSHelp(root, 0) 65 | return res 66 | } 67 | -------------------------------------------------------------------------------- /src/leetCode/build-an-array-with-stack-operations/build-an-array-with-stack-operations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} target 3 | * @param {number} n 4 | * @return {string[]} 5 | */ 6 | var buildArray = function (target, n) { 7 | let stack = [] 8 | let index = 0 // target指针 找到当前循环的目标值 9 | for (let i = 1; i <= n; i++) { 10 | // 添加target的元素 11 | stack.push('Push') 12 | if (target[index] > i) { 13 | // 如果目标元素比当前值大 添加了 删除 14 | stack.push('Pop') 15 | continue 16 | } 17 | index++ // 移动target指针 查找下一个target 18 | if (i === target[target.length - 1]) break // 当前循环值 就是目标值的最后一个元素 结束 19 | } 20 | return stack 21 | } 22 | -------------------------------------------------------------------------------- /src/leetCode/can-place-flowers/can-place-flowers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} flowerbed 3 | * @param {number} n 4 | * @return {boolean} 5 | * fn = fn-1 !== 1 && fn+1 !== 1 6 | */ 7 | var canPlaceFlowers = function (flowerbed, n) { 8 | let total = flowerbed.length 9 | for (let i = 0; i < total; i += 2) { 10 | // 0才考虑种花 11 | if (flowerbed[i] === 0) { 12 | // 下格为空 说明可种 末尾也说明可种 因为一下子跳两格 已经消除影响了 13 | if (flowerbed[i + 1] === 0 || i === total - 1) { 14 | n-- // 15 | } else { 16 | i++ // 下格为1 跳过本格 变成1那格 以及下次循环连跳两格 消除1的影响 17 | } 18 | } 19 | } 20 | return n <= 0 21 | } 22 | -------------------------------------------------------------------------------- /src/leetCode/climbing-stairs/climbing-stairs.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @param {number} n 4 | * @return {number} 5 | */ 6 | let climbStairs = function (n) { 7 | let dp = [0, 1, 2] // 初始0 1 2 个台阶没有规律 8 | // 从第三个开始有规律 9 | for (let i = 3; i <= n; i++) { 10 | dp[i] = dp[i - 1] + dp[i - 2] 11 | } 12 | return dp[n] 13 | } 14 | 15 | // 动态规划 变量交换 16 | // let climbStairs = function (n) { 17 | // let pre = 0; let 18 | // next = 1 // 初始值 19 | // // 每次循环算出 当次循环的值 20 | // for (let i = 0; i < n; i++) { 21 | // [next, pre] = [next + pre, next] 22 | // } 23 | // return next 24 | // } 25 | 26 | // 递归 27 | // const climbStairs = function (n) { 28 | // function item(n) { 29 | // // 递归退出条件 30 | // if (n === 1) return 1; 31 | // if (n === 2) return 2; 32 | // return item(n - 1) + item(n - 2); // 将递归到1个楼梯和两个楼梯 最后反推到n个楼梯 33 | // } 34 | // return item(n); 35 | // }; 36 | -------------------------------------------------------------------------------- /src/leetCode/coin-change/coin-change.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} coins 3 | * @param {number} amount 4 | * @return {number} 5 | */ 6 | // 解析:https://leetcode-cn.com/problems/last-stone-weight-ii/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-5lfv/ 7 | // 背包问题模板 8 | // 完全背包问题、最值问题: 外循环nums 内循环target target正序 且 target>= nums[i] 9 | // 状态转移方程:dp[i] = max/min (dp[i], dp[i-nums[i]]+1) // 本题:更新当前数额下使用的最小数目硬币 10 | let coinChange = function (coins, amount) { 11 | let max = amount + 1 12 | let dp = new Array(amount + 1).fill(max) 13 | // 0块钱 用0硬币 没毛病 14 | dp[0] = 0 // 初始化硬币的数量 用于累加 15 | for (const coin of coins.values()) { // 外循环 所有的硬币 也就是所有路径 16 | for (let i = 0; i <= amount; i++) { // 内循环目标值 每个目标值在不同硬币下 最小换几次 17 | if (coin <= i) { // 数额大于硬币 才可以用硬币兑换 18 | // 更新dp[i]使用不同coin的数量 19 | const lastCoinMin = dp[i - coin] 20 | // 使用不同coin 哪个最小 21 | dp[i] = Math.min(dp[i], lastCoinMin + 1) 22 | } 23 | } 24 | } 25 | return dp[amount] === max ? -1 : dp[amount] 26 | } 27 | 28 | // 动态规划 自下而上计算最小值 29 | // var coinChange = function (coins, amount) { 30 | // let max = amount + 1 // 每个硬币如果最小是1 不可能超过amount+1 31 | // // 初始化dp 32 | // let dp = new Array(amount + 1).fill(max) 33 | // dp[0] = 0 34 | // for (let i = 1; i <= amount; i++) { 35 | // // 计算每个金币的最小值 在i这个数量的最小值 方便更新最小值 36 | // for (let j = 0; j < coins.length; j++) { 37 | // let nowMoney = coins[j] 38 | // // 因为硬币不能拆开 所以当前硬币必须比i这个数量的钱小 才能计算当前硬币需要几个硬币 39 | // if (nowMoney <= i) { 40 | // let beforeMin = dp[i - nowMoney] // 动态规划 利用之前的计算:减去当前金币的钱 最小需要硬币是多少 41 | // let addBeforeMin = beforeMin + 1 // 加上当前的nowMoney的这枚硬币 得出目前在nowMoney下的最小硬币数量 42 | // // dp[i] 以每种金币为值 计算一遍最小值 如果后续有更小的 则更新dp[i] 43 | // dp[i] = Math.min(dp[i], addBeforeMin) 44 | // } 45 | 46 | // } 47 | // } 48 | // // 如果dp[amount] > max 说明未找到组成方案 因为初始化是最大值 49 | // return dp[amount] === max ? -1 : dp[amount] 50 | // }; 51 | -------------------------------------------------------------------------------- /src/leetCode/cong-wei-dao-tou-da-yin-lian-biao-lcof/cong-wei-dao-tou-da-yin-lian-biao-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | var reversePrint = function (head) { 10 | let curr = head 11 | let res = [] 12 | while (curr) { 13 | res.unshift(curr.val) // 从头部插入 14 | curr = curr.next 15 | } 16 | return res 17 | } 18 | -------------------------------------------------------------------------------- /src/leetCode/container-with-most-water/container-with-most-water.js: -------------------------------------------------------------------------------- 1 | // 双指针 O(n) 2 | function maxArea(height) { 3 | // 初始化双指针 4 | let r = height.length - 1 5 | let l = 0 6 | let max = 0 7 | while (l < r) { 8 | // 计算当前值 9 | const heightNum = height[l] > height[r] ? height[r] : height[l] 10 | const lengthNum = r - l 11 | // 最大值 12 | max = Math.max(max, heightNum * lengthNum) 13 | // 移动最小短板指针 14 | if (height[l] > height[r]) { 15 | r-- 16 | } else { 17 | l++ 18 | } 19 | } 20 | return max 21 | } 22 | 23 | 24 | // 双循环 O(2n) 25 | // function maxArea(height) { 26 | // let total = height.length 27 | // let max = 0 28 | // // 双循环 每个木板都跟其他木板匹配一次 29 | // for (let i = 0; i < total; i++) { 30 | // for (let j = 1; j < total; j++) { 31 | // // 两个木板的高度 32 | // let num1 = height[i] 33 | // let num2 = height[j] 34 | // // 获取最小高度 35 | // let heightNum = num1 > num2 ? num2 : num1 // 取木板最小的那个值 36 | // let lengthNum = j - i // 底部的长度 37 | // let size = heightNum * lengthNum 38 | // if (size > max) { 39 | // max = size // 最大面积 40 | // } 41 | // } 42 | // } 43 | // return max 44 | // } 45 | -------------------------------------------------------------------------------- /src/leetCode/contiguous-sequence-lcci/contiguous-sequence-lcci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | let maxSubArray = function (nums) { 6 | let max = nums[0] 7 | let before = nums[0] 8 | for (let i = 1; i < nums.length; i++) { 9 | // 当前值 与之前值加当前值比较 找出是否上升 10 | before = Math.max(nums[i], nums[i] + before) 11 | // 找出目前最大值 12 | max = Math.max(max, before) 13 | } 14 | return max 15 | } 16 | -------------------------------------------------------------------------------- /src/leetCode/convert-sorted-array-to-binary-search-tree/convert-sorted-array-to-binary-search-tree.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * 深度遍历 12 | * @param {number[]} nums 13 | * @return {TreeNode} 14 | */ 15 | var sortedArrayToBST = function (nums) { 16 | if (!nums.length) return null // 元素不存在 17 | let mid = Math.floor(nums.length / 2) 18 | let root = new TreeNode(nums[mid], null, null) // 取中间元素为根节点 19 | // 将数组分为两边 放入左右节点 会一直递归直到剩一个元素 或者不存在 放入孙子节点 20 | root.left = sortedArrayToBST(nums.slice(0, mid)) 21 | root.right = sortedArrayToBST(nums.slice(mid + 1)) 22 | return root 23 | } 24 | -------------------------------------------------------------------------------- /src/leetCode/count-the-repetitions/count-the-repetitions.js: -------------------------------------------------------------------------------- 1 | let getMaxRepetitions = function (s1, n1, s2, n2) { 2 | // 字符串转成数组 3 | let arr1 = s1.split('') 4 | let arr2 = s2.split('') 5 | let k = 0 // s2指针 6 | const S1Len = arr1.length * n1 // S1的长度 7 | for (let j = 0; j < S1Len; j++) { 8 | // 取余获得index 9 | const s1Index = j % arr1.length 10 | const s2Index = k % arr2.length 11 | if (arr1[s1Index] === arr2[s2Index]) k++ // 匹配到字符 指针增加 12 | } 13 | const S2Len = arr2.length * n2 // S2的长度 14 | return (k / S2Len) | 0 15 | } 16 | -------------------------------------------------------------------------------- /src/leetCode/delete-node-in-a-linked-list/delete-node-in-a-linked-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} node 10 | * @return {void} Do not return anything, modify node in-place instead. 11 | */ 12 | var deleteNode = function (node) { 13 | // 删除本节点 14 | node.val = node.next.val 15 | node.next = node.next.next 16 | } 17 | -------------------------------------------------------------------------------- /src/leetCode/deleteLowerStr/ 删除字符串中出现次数最少的字符.md: -------------------------------------------------------------------------------- 1 | 出自牛客网:https://www.nowcoder.com/practice/05182d328eb848dda7fdd5e029a56da9 2 | 3 | 描述 4 | 实现删除字符串中出现次数最少的字符,若出现次数最少的字符有多个,则把出现次数最少的字符都删除。输出删除这些单词后的字符串,字符串中其它字符保持原来的顺序。 5 | 6 | 数据范围:输入的字符串长度满足 1 \le n \le 20 \1≤n≤20 ,保证输入的字符串中仅出现小写字母 7 | 输入描述: 8 | 字符串只包含小写英文字母, 不考虑非法输入,输入的字符串长度小于等于20个字节。 9 | 10 | 输出描述: 11 | 删除字符串中出现次数最少的字符后的字符串。 12 | 13 | 示例: 14 | 15 | 输入: 16 | aabcddd 17 | 输出: 18 | aaddd -------------------------------------------------------------------------------- /src/leetCode/deleteLowerStr/index.js: -------------------------------------------------------------------------------- 1 | function handle(str) { 2 | } 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | // 答案慎看 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | // /** 52 | // * @description: 53 | // * 1. 思路通过对象的key设置数量 54 | // * 2. 获取对象中最小的值 55 | // * 3. 循环字符串,对比最小值与当前字符不相等 56 | // * 4. 可以输出。 57 | // * @param {type} str 58 | // * @return {type} res 59 | // */ 60 | // function handle(str) { 61 | // let obj = {} 62 | // let res = '' 63 | // for (let i = 0; i < str.length; i++) { 64 | // if (obj[str[i]]) obj[str[i]]++ 65 | // else obj[str[i]] = 1 66 | // } 67 | // let min = Math.min(...Object.values(obj)) 68 | // for (let i = 0; i < str.length; i++) { 69 | // if (obj[str[i]] !== min) res += str[i] 70 | // } 71 | // return res 72 | // } 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/leetCode/diameter-of-binary-tree/diameter-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number} 12 | */ 13 | var diameterOfBinaryTree = function (root) { 14 | let max = 0 // 树的最大直径 15 | let help = (node) => { 16 | if (!node) return 0 17 | let left = help(node.left) 18 | let right = help(node.right) 19 | max = Math.max(max, left + right) // 比较树的直径 20 | return Math.max(left, right) + 1 // 子树的深度 21 | } 22 | help(root) 23 | return max 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/divisor-game/divisor-game.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} N 3 | * @return {boolean} 4 | */ 5 | let divisorGame = function (N) { 6 | return N % 2 === 0 7 | } 8 | -------------------------------------------------------------------------------- /src/leetCode/editDiatance/editDiatance.js: -------------------------------------------------------------------------------- 1 | // 72. 编辑距离 - 困难 2 | 3 | const minDistance = (word1, word2) => { 4 | 5 | } 6 | 7 | console.log(minDistance('horse', 'ros')) // 3 8 | 9 | // word1 = "horse", word2 = "ros" 10 | // 3 11 | 12 | 13 | 14 | 15 | // 答案慎看 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | // /** 33 | // * @param {string} word1 34 | // * @param {string} word2 35 | // * @return {number} 36 | // */ 37 | // const minDistance = (word1, word2) => { 38 | // const dp = new Array(word1.length + 1) // 共有word1.length + 1行 39 | // for (let i = 0; i < dp.length; i++) { 40 | // dp[i] = new Array(word2.length + 1).fill(0) // 共有 word2.length +1 列 41 | // } 42 | 43 | // // 初始化数组,word1前i个字符最少需要i次操作,比如i次删除变成word2 44 | // for (let i = 1; i <= word1.length; i++) { 45 | // dp[i][0] = i 46 | // } 47 | 48 | // // 初始化数组,word2前j个字符最少需要j次操作,比如j次插入变成word1 49 | // for (let j = 1; j <= word2.length; j++) { 50 | // dp[0][j] = j 51 | // } 52 | 53 | // // 循环word1和word2 54 | // for (let i = 1; i <= word1.length; i++) { 55 | // for (let j = 1; j <= word2.length; j++) { 56 | // if (word1[i - 1] === word2[j - 1]) { 57 | // // 如果word1[i-1] === word2[j-1], 说明需要新增的字符相等,说明最后一个字符不用操作, 直接复用上一个字符的结果 58 | // dp[i][j] = dp[i - 1][j - 1] 59 | // } else { 60 | // // dp[i-1][j] + 1:对应删除 61 | // // dp[i][j-1] + 1:对应新增 62 | // // dp[i-1][j-1] + 1:对应替换操作 63 | // dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 64 | // } 65 | // } 66 | // } 67 | 68 | // return dp[word1.length][word2.length] 69 | // } 70 | -------------------------------------------------------------------------------- /src/leetCode/editDiatance/index.md: -------------------------------------------------------------------------------- 1 | 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。 2 | 3 | 你可以对一个单词进行如下三种操作: 4 | 5 | 插入一个字符 6 | 删除一个字符 7 | 替换一个字符 8 | 9 | 1. 10 | 11 | 输入:word1 = "horse", word2 = "ros" 12 | 输出:3 13 | 解释: 14 | horse -> rorse (将 'h' 替换为 'r') 15 | rorse -> rose (删除 'r') 16 | rose -> ros (删除 'e' 17 | 18 | 来源:力扣(LeetCode) 19 | 链接:https://leetcode.cn/problems/edit-distance 20 | 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 21 | 22 | 2. 23 | 24 | 输入:word1 = "intention", word2 = "execution" 25 | 输出:5 26 | 解释: 27 | intention -> inention (删除 't') 28 | inention -> enention (将 'i' 替换为 'e') 29 | enention -> exention (将 'n' 替换为 'x') 30 | exention -> exection (将 'n' 替换为 'c') 31 | exection -> execution (插入 'u') 32 | 33 | tip: 34 | 35 | 0 <= word1.length, word2.length <= 500 36 | word1 和 word2 由小写英文字母组成 -------------------------------------------------------------------------------- /src/leetCode/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @param {number} k 11 | * @return {number} 12 | */ 13 | var kthLargest = function (root, k) { 14 | let res 15 | const getVal = (root) => { 16 | if (!root) return 17 | if (res) return 18 | // 从大到小 先搜索大的树 搜索到尽头就是最大的 19 | getVal(root.right) 20 | // 添加 21 | k-- // 每拿到一个最大值 就减一 22 | // 直到拿到第K大的值 即找到 23 | if (k === 0) { 24 | res = root.val 25 | } 26 | // 搜索小的树 27 | getVal(root.left) 28 | } 29 | getVal(root) 30 | return res 31 | } 32 | -------------------------------------------------------------------------------- /src/leetCode/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * // Definition for a Node. 3 | * function Node(val,left,right) { 4 | * this.val = val; 5 | * this.left = left; 6 | * this.right = right; 7 | * }; 8 | */ 9 | /** 10 | * @param {Node} root 11 | * @return {Node} 12 | */ 13 | // 深度递归 拿到头部 链接前一个值 改变指针 退出当前函数 在第二个函数一开始的时候 链接后一个值 14 | // 递归结束在拿到尾部 链接头尾 15 | var treeToDoublyList = function (root) { 16 | if (!root) return null 17 | let head = null 18 | let pre = null 19 | dfs(root) 20 | // pre是尾部 递归结束 21 | // 链接头尾 22 | head.left = pre 23 | pre.right = head 24 | function dfs(node) { 25 | if (!node) return 26 | dfs(node.left) 27 | if (pre) { 28 | // 链接一边2:有前一个值 前一个值链接后一个值 29 | pre.right = node 30 | } else { 31 | // 找到链表头部:进入 深度递归 找到最深一层 没有前一个值 找到head 32 | head = node 33 | } 34 | // 链接一边1, 更改指针 退出函数 链接另一边 35 | node.left = pre 36 | pre = node 37 | dfs(node.right) 38 | } 39 | return head 40 | } 41 | 42 | // 栈递归 43 | // var treeToDoublyList = function (root) { 44 | // if(!root) return null 45 | // let stack = [] 46 | // let head = null 47 | // let pre = null 48 | // let cur = root 49 | // while (cur || stack.length) { 50 | // // 递归找节点 51 | // while (cur) { 52 | // stack.push(cur) 53 | // cur = cur.left 54 | // } 55 | // cur = stack.pop() // 取出最后面的节点 56 | // if (pre) { 57 | // pre.right = cur 58 | // } else { 59 | // head = cur 60 | // } 61 | // // 链接节点1 62 | // cur.left = pre 63 | // pre = cur 64 | // cur = cur.right // 修改指针 取出右节点 65 | // } 66 | // // pre尾节点 67 | // head.left = pre 68 | // pre.right = head 69 | // return head 70 | // } 71 | -------------------------------------------------------------------------------- /src/leetCode/fei-bo-na-qi-shu-lie-lcof/fei-bo-na-qi-shu-lie-lcof.js: -------------------------------------------------------------------------------- 1 | // 递归解法: 2 | const fibonacci = (n) => { 3 | if (!(typeof n === 'number' && n % 1 === 0 && n > 1)) { 4 | throw '请输入大于0的整数数字' 5 | } 6 | let array = [0, 0, 1] 7 | let temp = (n) => { 8 | if (n === 1 || n === 2) return array[n] 9 | array[n] = temp(n - 1) + temp(n - 2) // 递归获取推算数组每一个元素的值 10 | return array[n] 11 | } 12 | let num = temp(n) 13 | array.splice(2, 1) // 将数组恢复成 斐波纳契数列 14 | return num 15 | } 16 | 17 | // // 遍历保存结果 18 | // const fibonacci = n => { 19 | // let a = 0, 20 | // b = 1, 21 | // c, 22 | // d = [0]; 23 | // for (let i = 1; i < n; i++) { 24 | // c = a + b; 25 | // a = b; 26 | // b = c; 27 | // d.push(a); // 加戏 恢复数列 28 | // } 29 | // console.log(d, '斐波纳契数列'); 30 | // return a; 31 | // }; 32 | 33 | 34 | 35 | // // 一次遍历 逐步推导所有元素 36 | // const fibonacci = n => { 37 | // let num = new Array(n).fill(0); // 初始化数组,并设置初始值 38 | // num[1] = 1; // 设置第二个元素的值 推导第3个元素 39 | // for (let i = 2; i <= n - 1; i++) { 40 | // num[i] = num[i - 2] + num[i - 1]; // 遍历逐步推导元素值 数组完全符合数列不用进行判断等 运行效率最高。 41 | // } 42 | // return num[n - 1]; // 数组是从0开始计算 所以要减1 43 | // }; 44 | -------------------------------------------------------------------------------- /src/leetCode/flatten-binary-tree-to-linked-list/flatten-binary-tree-to-linked-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {void} Do not return anything, modify root in-place instead. 12 | */ 13 | // 不能使用先序遍历 因为先序遍历 会导致右侧孩子节点丢失(被右指针覆盖了)。 14 | // 后序遍历 右左根 15 | // 每遍历一个节点就将当前节点的右指针(比当前节点小) 指向上一个节点 因为是后序遍历 所以上一个节点就是比它大的值 16 | var flatten = function (root) { 17 | if (!root) return null 18 | let pre = null 19 | dfs(root) 20 | function dfs(node) { 21 | if (!node) return 22 | dfs(node.right) 23 | dfs(node.left) 24 | node.right = pre // 当前节点的右指针 指向上一个节点 上一个节点就是后续遍历 最后一个值 6 5 4 3 2 1 25 | node.left = null // 左指针清空 26 | pre = node // 缓存节点 27 | } 28 | return pre // 最后pre是root节点 也就是head 29 | } 30 | 31 | // 栈:思路:将左子树接到右子树上 将右子树接到左子树后面 32 | // var flatten = function (root) { 33 | // while (root) { 34 | // if (!root.left) { 35 | // // 左子树没值 循环下一个右节点 36 | // root = root.right 37 | // } else { 38 | // // 找左子树的最右边的节点 也就是最大的节点 39 | // let pre = root.left 40 | // while (pre.right) { 41 | // pre = pre.right 42 | // } 43 | // pre.right = root.right 44 | // // 将左子树插入到右子树的地方 45 | // root.right = root.left 46 | // root.left = null 47 | // // 遍历下一个节点 下一个节点的左子树还没插入到右子树。 48 | // root = root.right 49 | // } 50 | // } 51 | // return root 52 | // } 53 | -------------------------------------------------------------------------------- /src/leetCode/gas-station/gas-station.js: -------------------------------------------------------------------------------- 1 | 2 | var canCompleteCircuit = function (gas, cost) { 3 | let cursum = 0 // 记录油箱中的油量 4 | let sum = 0 // 路程总消耗 5 | let start = 0 // 起点 6 | for (let i = 0; i < gas.length; i++) { 7 | cursum += gas[i] - cost[i] // 8 | sum += gas[i] - cost[i] 9 | if (cursum < 0) { 10 | // 无法从 start 到达 i + 1 11 | // 所以站点 i + 1 应该是起点 12 | start = i + 1 13 | cursum = 0 14 | } 15 | } 16 | if (sum < 0) return -1 // 总汽油花费 大于路程花费 17 | return start 18 | } 19 | 20 | 21 | // /** 22 | // * 贪心 23 | // * @param {number[]} gas 24 | // * @param {number[]} cost 25 | // * @return {number} 26 | // */ 27 | // var canCompleteCircuit = function (gas, cost) { 28 | // let len = gas.length 29 | // let spare = 0 // 剩余油量 30 | // let minSpare = 'init' // 上一轮花费油费 比充油多的情况 保存所有剩余油量 31 | // let minIndex = 0 // 这个值的下一个值就是充油比花费多的情况 32 | // for (let i = 0; i < len; i++) { 33 | // spare += gas[i] - cost[i] // 每个站点的油量剩余相加 34 | // // 本次花费油量比充的油量要多 35 | // if (minSpare === 'init' || spare < minSpare) { 36 | // // 重置当前最低油量 遇到正值才会加 37 | // minSpare = spare 38 | // minIndex = i // 下一个值为起点 39 | // } 40 | // } 41 | // // 每个站点的油量剩余相加 如果最后小于0 代表无解 大于0 代表有解 42 | // return spare < 0 ? -1 : (minIndex + 1) % len 43 | // } 44 | -------------------------------------------------------------------------------- /src/leetCode/generate-parentheses/generate-parentheses.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {string[]} 4 | */ 5 | // 树思想 回溯 6 | let generateParenthesis = function (n) { 7 | let left = n // 左右分支的数量 8 | let right = n // 左右分支的数量 9 | let res = [] 10 | if (n === 0) { 11 | return res 12 | } 13 | dfs('', left, right) 14 | function dfs(preStr, left, right) { 15 | // 当没有括号时 即回溯终止 16 | if (left === 0 && right === 0) { 17 | res.push(preStr) 18 | return 19 | } 20 | // 当成一颗深度为2n的树来做 每个括号在这棵树内都会都用到 21 | // 剪枝: 左括号可以使用的个数严格大于右括号可以使用的个数时 左侧已经准备使用该括号了 22 | if (left > right) { 23 | return 24 | } 25 | // 一次添加左侧一次添加右侧 回溯 凑成括号 26 | if (left > 0) { 27 | dfs(`${preStr}(`, left - 1, right) 28 | } 29 | if (right > 0) { 30 | dfs(`${preStr})`, left, right - 1) 31 | } 32 | } 33 | return res 34 | } 35 | -------------------------------------------------------------------------------- /src/leetCode/hanota-lcci/hanota-lcci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 从最后一根柱子移动到第一根柱子 3 | * 结束条件 A B 没有元素 4 | * 归纳 5 | * @param {number[]} A 6 | * @param {number[]} B 7 | * @param {number[]} C 8 | * @return {void} Do not return anything, modify C in-place instead. 9 | */ 10 | var hanota = function (A, B, C) { 11 | let n = A.length 12 | let move = function (m, a, b, c) { 13 | if (m === 1) { // 当只有一个时直接加到c中 14 | c.push(a.pop()) 15 | } else { 16 | move(m - 1, a, c, b) // 将 a 上的 n - 1 个 通过 c 移到 b 17 | c.push(a.pop()) // 把 a 中剩下的一个直接放到 c 18 | move(m - 1, b, a, c) // 在把 b 上的 n - 1 个 通过 a 放到 c 19 | } 20 | } 21 | move(n, A, B, C) 22 | } 23 | -------------------------------------------------------------------------------- /src/leetCode/house-robber-ii/house-robber-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态规划 3 | * 不偷头部与不偷尾部分别算出 相比较 哪个更多 4 | * @param {number[]} nums 5 | * @return {number} 6 | */ 7 | let rob = function (nums) { 8 | if (nums.length === 0) return 0 9 | if (nums.length === 1) return nums[0] 10 | // 不偷尾部 11 | let noEnd = help(0, nums.length - 2) 12 | // 不偷头部 13 | let noHead = help(1, nums.length - 1) 14 | // 获取一段下标的最大值 变量版本 15 | // function help(start, end) { 16 | // let prev = 0 // 上上个值 17 | // let curr = 0 // 上个值 18 | // while (start <= end) { 19 | // // 当前最大值 = 上个最大值 与 上上个值 + 当前值 比较 20 | // let temp = Math.max(curr, prev + nums[start]) 21 | // prev = curr // 上个值变成上上个值 22 | // curr = temp // 当前值变成上上个值 23 | // start++ // 增加指针 24 | // } 25 | // return curr 26 | // } 27 | // 获取一段下标的最大值 dp版本 最重要的是设置前面两个初始值 28 | function help(start, end) { 29 | let dp = [] // 对应数量的房间 能偷的最大值 30 | // 最重要的是设置前面两个初始值 31 | dp[start] = nums[start] // 起始的最大值 32 | dp[start + 1] = Math.max(nums[start], nums[start + 1]) // 第二个值的最大值 33 | for (let i = start + 2; i <= end; i++) { 34 | dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]) 35 | } 36 | return dp[end] 37 | } 38 | // 不偷头部与不偷尾部相比较 哪个更多 39 | return Math.max(noEnd, noHead) 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/leetCode/house-robber-iii/house-robber-iii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * dfs 深度优先 11 | * @param {TreeNode} root 12 | * @return {number} 13 | */ 14 | let rob = function (root) { 15 | const dfs = (node) => { 16 | // 边界条件 17 | if (!node) return [0, 0] 18 | // dfs倒推:left = right = [不偷当前节点的最大值, 偷当前节点的最大值] 19 | let left = dfs(node.left) 20 | let right = dfs(node.right) 21 | let max = [] 22 | // 不偷当前节点的最大值:根据左右儿子偷与不偷的最大值的大小来选择左右儿子偷不偷 23 | max[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]) 24 | // 偷当前节点的最大值:不偷两个孩子的最大值 + 本身的值 25 | max[1] = left[0] + right[0] + node.val 26 | return max 27 | } 28 | // 获取偷与不偷的最大值 29 | let maxArr = dfs(root) 30 | return Math.max(...maxArr) 31 | } 32 | -------------------------------------------------------------------------------- /src/leetCode/house-robber/house-robber.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态规划 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | let rob = function (nums) { 7 | for (let i = 0; i < nums.length; i++) { 8 | // 0的时候 直接取第一个 9 | if (i === 0) { 10 | nums[0] = nums[i] 11 | } else if (i === 1) { 12 | // 1的时候 对比前两个 13 | nums[i] = Math.max(nums[0], nums[1]) 14 | } else { 15 | // 上上一个值的最大值 + 当前值 即为当前位置的最大值 16 | let now = nums[i - 2] + nums[i] 17 | // 对比上一个最大值 与当前最大值 相比 取最大的 18 | nums[i] = Math.max(now, nums[i - 1]) 19 | } 20 | } 21 | return nums[nums.length - 1] 22 | } 23 | 24 | 25 | // /** 26 | // * 动态规划 空间优化 27 | // * @param {number[]} nums 28 | // * @return {number} 29 | // */ 30 | // var rob = function (nums) { 31 | // let prev = 0 // 上上个值 32 | // let curr = 0 // 上个值 33 | // for (let i = 0; i < nums.length; i++) { 34 | // // 上上一个值的最大值 + 当前值 即为当前位置的最大值 35 | // let now = prev + nums[i] 36 | // // 对比上上房间+当前房间 与上一间最大值 相比 取最大的 37 | // prev = curr 38 | // curr = Math.max(curr, now) // 当前计算的房间 39 | // } 40 | // return curr 41 | // } 42 | 43 | -------------------------------------------------------------------------------- /src/leetCode/implement-queue-using-stacks/implement-queue-using-stacks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initialize your data structure here. 3 | */ 4 | var MyQueue = function () { 5 | this.stack = [] 6 | this.outStack = [] 7 | } 8 | 9 | /** 10 | * Push element x to the back of queue. 11 | * @param {number} x 12 | * @return {void} 13 | */ 14 | MyQueue.prototype.push = function (x) { 15 | this.stack.push(x) 16 | } 17 | 18 | /** 19 | * Removes the element from in front of queue and returns that element. 20 | * @return {number} 21 | */ 22 | MyQueue.prototype.pop = function () { 23 | // 输出栈为空 则将输入栈弹入输入栈 更改顺序为从尾到头 24 | if (!this.outStack.length) { 25 | this.in2out() 26 | } 27 | return this.outStack.pop() // 从队列头先输出 28 | } 29 | 30 | /** 31 | * Get the front element. 32 | * @return {number} 33 | */ 34 | MyQueue.prototype.peek = function () { 35 | // 输出栈为空 则将输入栈弹入输入栈 更改顺序为从尾到头 36 | if (!this.outStack.length) { 37 | this.in2out() 38 | } 39 | return this.outStack[this.outStack.length - 1] // 返回队列头元素 40 | } 41 | 42 | /** 43 | * Returns whether the queue is empty. 44 | * @return {boolean} 45 | */ 46 | MyQueue.prototype.empty = function () { 47 | return this.stack.length === 0 && this.outStack.length === 0 48 | } 49 | 50 | // 更改第一个栈中的顺序 从尾到头 51 | MyQueue.prototype.in2out = function () { 52 | if (this.stack.length) { 53 | while (this.stack.length) { 54 | this.outStack.push(this.stack.pop()) 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * Your MyQueue object will be instantiated and called as such: 61 | * var obj = new MyQueue() 62 | * obj.push(x) 63 | * var param_2 = obj.pop() 64 | * var param_3 = obj.peek() 65 | * var param_4 = obj.empty() 66 | */ 67 | -------------------------------------------------------------------------------- /src/leetCode/intersection-of-two-arrays-ii/intersection-of-two-arrays-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums1 3 | * @param {number[]} nums2 4 | * @return {number[]} 5 | */ 6 | 7 | // 排序+双指针 自己想的 8 | // var intersect = function (nums1, nums2) { 9 | // // 排序 10 | // nums1.sort((a, b) => a - b) 11 | // nums2.sort((a, b) => a - b) 12 | // let res = [] 13 | // let first = 0, cur = 0 // 第二个指针 14 | // while (first < nums1.length && cur < nums2.length) { 15 | // if (nums1[first] > nums2[cur]) { 16 | // // 第一个数组元素比较大 移动第二个元素的指针 17 | // cur++ 18 | // } else if (nums1[first] === nums2[cur]) { 19 | // // 相同 添加到结果中 并且移动指针 20 | // res.push(nums1[first]) 21 | // cur++ 22 | // first++ 23 | // } else { 24 | // // nums1[first] 比较小 增加指针 25 | // first++ 26 | // } 27 | // } 28 | // return res 29 | // }; 30 | 31 | // 哈希表 计数添加 32 | var intersect = function (nums1, nums2) { 33 | let obj = new Map() 34 | let res = [] 35 | // 计算nums1中元素的数量 36 | for (let i = 0; i < nums1.length; i++) { 37 | let currt = nums1[i] 38 | if (obj.has(currt)) { 39 | let total = obj.get(currt) 40 | obj.set(currt, total + 1) 41 | } else { 42 | obj.set(currt, 1) 43 | } 44 | } 45 | // 计算nums2中是否存在 如果存在数量减一并添加到res中 46 | for (let i = 0; i < nums2.length; i++) { 47 | let currt = nums2[i] 48 | if (obj.has(currt)) { 49 | let total = obj.get(currt) 50 | obj.set(currt, total - 1) 51 | res.push(currt) 52 | if (total - 1 === 0) obj.delete(currt) 53 | } 54 | } 55 | return res 56 | } 57 | -------------------------------------------------------------------------------- /src/leetCode/intersection-of-two-arrays/intersection-of-two-arrays.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {number[]} nums1 4 | * @param {number[]} nums2 5 | * @return {number[]} 6 | */ 7 | // 1. 哈希表 Set 数据结构 // 提示 我只想到了对象 8 | function intersection(nums1, nums2) { 9 | let resSet = new Set() 10 | let nums1Set = new Set(nums1) 11 | for (let i of nums2) { 12 | if (nums1Set.has(i)) { 13 | resSet.add(i) 14 | } 15 | } 16 | return Array.from(resSet) 17 | } 18 | 19 | // // 2. includes 20 | // function intersection(nums1, nums2) { 21 | // return Array.from(new Set(nums1.filter(i => nums2.includes(i)))) 22 | // }; 23 | 24 | 25 | // // 3. 排序+双指针 可添加重复元素的方法 自己写的 26 | // var intersection = function (nums1, nums2) { 27 | // let res = [] 28 | // // 排序 29 | // nums1.sort((a, b) => a - b) 30 | // nums2.sort((a, b) => a - b) 31 | // let cur = 0 // 数组2的指针 32 | // for (let i = 0; i < nums1.length; i++) { 33 | // // 数组2的元素 小于数组1元素 表示它们不匹配 34 | // while (nums2[cur] < nums1[i]) { 35 | // cur++ // 移动指针 36 | // // 指针超过 匹配结束 37 | // if (cur === nums2.length) { 38 | // return res 39 | // } 40 | // } 41 | // if (res.length !== 0 && res[res.length - 1] === nums1[i]) continue // 不添加重复元素 42 | // // nums2元素比较大 移动nums1指针 43 | // if (nums2[cur] > nums1[i]) { 44 | // continue 45 | // } else if (nums2[cur] === nums1[i]) { 46 | // // 相等 则添加 47 | // res.push(nums1[i]) 48 | // } 49 | // cur++ // 移动指针 50 | // } 51 | // return res 52 | // } 53 | -------------------------------------------------------------------------------- /src/leetCode/intersection-of-two-linked-lists/intersection-of-two-linked-lists.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | /** 10 | * @param {ListNode} headA 11 | * @param {ListNode} headB 12 | * @return {ListNode} 13 | */ 14 | // 哈希法 15 | // 1. 以链表为key 16 | // 2. 查找到相同链表 即为相交点 17 | // var getIntersectionNode = function (headA, headB) { 18 | // let m = new Map() // 以链表为key 19 | // let curr = headA 20 | // while (curr) { 21 | // m.set(curr, curr) 22 | // curr = curr.next 23 | // } 24 | // curr = headB 25 | // while (curr) { 26 | // // 查找到相同链表 即为相交点 27 | // if (m.get(curr)) { 28 | // return curr 29 | // } 30 | // curr = curr.next 31 | // } 32 | // return null 33 | // } 34 | 35 | // 双指针 36 | // 头节点 headA 到 node 前,共有 a - c 个节点; 37 | // 头节点 headB 到 node 前,共有 b - c 个节点; 38 | // 一长一短的话, 短的先结束变成长的 继续移动节点 这样等长的结束后 变成短的链表 它们的节点数量就相同了(短的变成长的 一直在移动 长的刚变成短的) 39 | // 节点数量相同,一起移动节点就会遇到相交的节点了 40 | var getIntersectionNode = function (headA, headB) { 41 | let A = headA; let 42 | B = headB 43 | while (A !== B) { 44 | if (A !== null) { 45 | A = A.next 46 | } else { 47 | // A到结尾 变成B链表 48 | A = headB 49 | } 50 | if (B !== null) { 51 | B = B.next 52 | } else { 53 | // B到结尾变成A链表 54 | B = headA 55 | } 56 | } 57 | return A 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/leetCode/invert-binary-tree/invert-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | 10 | // 递归 从上到下翻转 11 | var invertTree = function (root) { 12 | // 递归终止 13 | if (!root) return root 14 | // 交换上级的左右节点 15 | let temp = root.left 16 | root.left = root.right 17 | root.right = temp 18 | // 递归交换子节点的左右节点 19 | invertTree(root.left) 20 | invertTree(root.right) 21 | return root // 返回交换过的左右节点 用于组装 22 | } 23 | 24 | // 广度优先 从上到下 25 | // var invertTree = function (root) { 26 | // if (!root) return root 27 | // let stack = [root] // 栈 28 | // while (stack.length) { 29 | // let len = stack.length 30 | // // 遍历同一层级的所有节点 31 | // for (let i = 0; i < len; i++) { 32 | // let node = stack.shift() // 从开头取元素 先进先出 33 | // if (!node) continue // 如果节点为空 则不操作 34 | // // 交换节点 35 | // let temp = node.left 36 | // node.left = node.right 37 | // node.right = temp 38 | // // 在栈后面添加左右节点 39 | // stack.push(node.left, node.right) 40 | // } 41 | // } 42 | // return root 43 | // } 44 | 45 | // 递归 从下到上翻转 46 | // var invertTree = function (root) { 47 | // // 递归终止 48 | // if (!root) return root 49 | // // 递归 获取子级已经交换左右节点的root 左右组装root 50 | // let left = invertTree(root.left) 51 | // let right = invertTree(root.right) 52 | // // 本节点下面的子节点都已经翻转 实现本节点下面的翻转 53 | // root.left = right 54 | // root.right = left 55 | // return root // 返回已经交换左右节点的root 56 | // }; 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/leetCode/is-subsequence/is-subsequence.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @param {string} t 4 | * @return {boolean} 5 | */ 6 | 7 | /** 8 | * @param {string} s 9 | * @param {string} t 10 | * @return {boolean} 11 | */ 12 | 13 | // 双指针 14 | let isSubsequence = function (s, t) { 15 | const sLength = s.length 16 | const tLength = t.length 17 | let s1 = 0 18 | let t1 = 0 19 | while (s1 < sLength && t1 < t.length) { 20 | // 查找s的字符 是否全都存在于t上 21 | if (s[s1] === t[t1]) { 22 | s1++ // 查找下一个s字符 23 | // 全都查找到s1的长度等于sLength 24 | if (s1 === sLength) { 25 | return true // 提前退出循环 26 | } 27 | } 28 | t1++ // 指针移动 29 | } 30 | return s1 === sLength 31 | } 32 | 33 | // 循环查找给定字符串 34 | // var isSubsequence = function (s, t) { 35 | // let index = 0; 36 | // for (let i = 0; i < s.length; i++) { 37 | // // 循环给定字符串 在t中按查找过的索引查找每个字符 38 | // const strIndex = t.indexOf(s[i], index) 39 | // if (strIndex !== -1) { 40 | // // 更新下一次查找位置 从当前索引的下个字符开始查找 41 | // index = strIndex + 1 42 | // } else { 43 | // // 没找到即说明 不是子序列 44 | // return false 45 | // } 46 | // } 47 | // return true 48 | // }; 49 | -------------------------------------------------------------------------------- /src/leetCode/island-perimeter/island-perimeter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} grid 3 | * @return {number} 4 | */ 5 | var islandPerimeter = function (grid) { 6 | let len1 = grid.length 7 | let len2 = grid[0].length 8 | let res = 0 9 | // 遍历所有格子 10 | for (let i = 0; i < len1; i++) { 11 | for (let j = 0; j < len2; j++) { 12 | if (grid[i][j] === 1) { 13 | // 只有一个岛屿 直接return 14 | res += help(i, j) 15 | } 16 | } 17 | } 18 | // 计算单个格子的墙 19 | function help(i, j) { 20 | let one = 4 // 初始化一个格子四道墙 21 | // 碰到周边格子是1 则减1 22 | if (i > 0 && grid[i - 1][j] === 1) { 23 | one-- 24 | } 25 | if (i < len1 - 1 && grid[i + 1][j] === 1) { 26 | one-- 27 | } 28 | if (j > 0 && grid[i][j - 1] === 1) { 29 | one-- 30 | } 31 | if (j < len2 - 1 && grid[i][j + 1] === 1) { 32 | one-- 33 | } 34 | return one 35 | } 36 | return res 37 | } 38 | -------------------------------------------------------------------------------- /src/leetCode/jump-game/jump-game.js: -------------------------------------------------------------------------------- 1 | // 贪心 2 | let canJump = function (nums) { 3 | let max = 0 4 | for (let i = 0; i < nums.length; i++) { 5 | if (i > max) return false // 最远距离不能到达当前位置 6 | max = Math.max(max, i + nums[i]) // 更新最远距离 7 | if (max >= nums.length - 1) return true // 大于等于最远位置 即成功 8 | } 9 | } 10 | 11 | // var canJump = function (nums) { 12 | // let canJumpMax = 0 // 新的最远距离 当前遍历 13 | // let last_canJumpMax = 0 // 当前最远距离 14 | // let len = nums.length 15 | // for (let i = 0; i < len; i++) { 16 | // // 获取新的最远距离 以备到达最远距离后更新 17 | // canJumpMax = Math.max(canJumpMax, i + nums[i]) 18 | // if (last_canJumpMax === i) { 19 | // // 到达当前最远距离 20 | // // 更新能到达的最远距离 21 | // last_canJumpMax = canJumpMax 22 | // } else if (last_canJumpMax < i) { 23 | // // 超出能到达的最远距离 false 24 | // return false 25 | // } 26 | // } 27 | // return true 28 | // } 29 | -------------------------------------------------------------------------------- /src/leetCode/kth-largest-element-in-an-array/kth-largest-element-in-an-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} k 4 | * @return {number} 5 | */ 6 | const findKthLargest = function (nums, n) { 7 | let value 8 | // 遍历n次,移除n个最大值,最终value即为第n大元素 9 | for (let i = 0; i < n; i++) { 10 | let item = Math.max(...nums) // 取出最大值 11 | value = nums.splice(nums.indexOf(item), 1)[0] // 删除并保存最大值 12 | } 13 | return value 14 | } 15 | 16 | // /** 17 | // * 各种排序方法 然后获取第n个元素 18 | // * @param {number[]} nums 19 | // * @param {number} k 20 | // * @return {number} 21 | // */ 22 | // const findKthLargest = function(nums, n) { 23 | // // 排序替换 24 | // nums.sort((a, b) => { 25 | // return b - a; 26 | // }); 27 | // return nums[n - 1]; // 第n大(数组从0开始) 28 | // }; 29 | -------------------------------------------------------------------------------- /src/leetCode/largest-number/largest-number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 1. 转字符串 3 | * 2. 重点: sort排序, 两个值合并分别比较,倒序排序 4 | * 3. 最后将其组合起来 5 | * @param {number[]} nums 6 | * @return {string} 7 | */ 8 | var largestNumber = function (nums) { 9 | // 转字符串操作 10 | nums = nums.map((item) => String(item)) 11 | // sort排序, 12 | // 重点是:两个值合并分别比较,倒序排序 13 | // 对于 [4,42][4,42],比较 442 > 424442>424,需要把 44 放在前面; 14 | // 对于 [4,45][4,45],比较 445 < 454445<454,需要把 4545 放在前面。 15 | nums.sort((a, b) => { 16 | let res1 = a + b 17 | let res2 = b + a 18 | return res2 - res1 19 | }) 20 | // 前导0的情况 [ 0, 0] 情况 21 | if (nums[0] === '0') return '0' 22 | // 组合字符串 23 | return nums.join('') 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/largest-perimeter-triangle/largest-perimeter-triangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | 6 | // 三角形:1. a <= b <= c 2. a+b >c 7 | // 排序+贪心 8 | var largestPerimeter = function (nums) { 9 | nums.sort((a, b) => a - b) // 排序 10 | // 倒序 从最长长度开始计算 11 | for (let i = nums.length - 1; i >= 2; i--) { 12 | if (nums[i - 2] + nums[i - 1] > nums[i]) { 13 | // 第一个满足条件的即为最大周长 14 | return nums[i - 2] + nums[i - 1] + nums[i] 15 | } 16 | } 17 | return 0 18 | } 19 | -------------------------------------------------------------------------------- /src/leetCode/last-stone-weight-ii/last-stone-weight-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} stones 3 | * @return {number} 4 | */ 5 | // https://leetcode-cn.com/problems/last-stone-weight-ii/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-5lfv/ 6 | // 背包问题 套模板 7 | // 0/1背包 最值问题 8 | // 外循环nums 内循环target target倒序 且 target >= nums[i] 9 | // 状态方程:dp[i] = max/min(dp[i], dp[i - nums[i]] + nums[i]) // 当前题目下:更新空间所能承受的最大重量 10 | let lastStoneWeightII = function (stones) { 11 | let total = 0 // 总数 12 | for (let i = 0; i < stones.length; i++) { 13 | total += stones[i] 14 | } 15 | let target = Math.floor(total / 2) // 差值小 差值都要接近sum /2 16 | // 初始化dp 最后要取target的dp值 所以dp容量要以target为限 17 | let dp = new Array(target + 1).fill(0) 18 | for (const stone of stones.values()) { // 循环数目 19 | for (let i = target; i >= stone; i--) { // 中间值比数目大 在当前这个数目下 中间值的每个dp[i]可以放多重 20 | // 当前空间情况下使用目前这个重量数目下可以放多重的物品 如果比以前的数目重 则更新当前空间的重量 21 | dp[i] = Math.max(dp[i], dp[i - stone] + stone) 22 | } 23 | } 24 | return total - 2 * dp[target] 25 | } 26 | -------------------------------------------------------------------------------- /src/leetCode/last-stone-weight/last-stone-weight.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} stones 3 | * @return {number} 4 | */ 5 | 6 | // 贪心方式 7 | var lastStoneWeight = function (stones) { 8 | if (stones.length <= 1) return stones[0] 9 | stones.sort((a, b) => b - a) // 排序 从最大的开始往下粉碎 10 | let len = stones.length 11 | // 每次循环去掉一块石头 12 | while (len--) { 13 | stones[0] -= stones[1] // 每次从最大的开始往下粉碎 14 | stones[1] = 0 // 去掉一块石头 往后放 15 | stones.sort((a, b) => b - a) // 排序 从最大的开始往下粉碎 16 | } 17 | return stones[0] 18 | } 19 | 20 | // 数组 栈方式 21 | // var lastStoneWeight = function (stones) { 22 | // if (stones.length === 0) return 0 23 | // stones.sort((a, b) => b - a) // 排序 从最大的开始往下粉碎 24 | // while (stones.length > 1) { 25 | // let a = stones.shift() 26 | // let b = stones.shift() 27 | // // 每次粉碎两个 28 | // const num = Math.abs(a - b) 29 | // // 添加一个结果 30 | // stones.push(num) 31 | // stones.sort((a, b) => b - a) // 排序 从最大的开始往下粉碎 32 | // } 33 | // return stones.shift() 34 | // }; 35 | -------------------------------------------------------------------------------- /src/leetCode/lemonade-change/lemonade-change.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} bills 3 | * @return {boolean} 4 | */ 5 | 6 | // 官方找零 7 | var lemonadeChange = function (bills) { 8 | let five = 0; let 9 | ten = 0 // 五块钱和十块的数量 10 | for (const bill of bills.values()) { 11 | if (bill === 5) { 12 | five++ 13 | } else if (bill === 10) { 14 | five-- 15 | ten++ 16 | } else if (bill === 20) { 17 | if (ten) { 18 | // 有十块 优先用十块 19 | five-- 20 | ten-- 21 | } else { 22 | // 都用五块的 23 | five -= 3 24 | } 25 | } 26 | // 如果数量小于0 则找零失败 27 | if (five < 0 || ten < 0) return false 28 | } 29 | return true 30 | } 31 | 32 | 33 | // 贪心双循环找零 自己写的 34 | // var lemonadeChange = function (bills) { 35 | // let moneyArr = []; 36 | // // 排队 37 | // for (let i = 0; i < bills.length; i++) { 38 | // // 找零 39 | // if (bills[i] !== 5) { 40 | // const manMoney = bills[i] - 5 // 减去成本 41 | // if (!getMoney(manMoney)) return false // 找零失败 42 | // } 43 | // // 收钱 20块钱不要 因为不能用它来找零 44 | // if (bills[i] === 10) { 45 | // moneyArr.unshift(bills[i]) // 十块钱放在前面 优先用它找零 46 | // } else if (bills[i] === 5) { 47 | // moneyArr.push(bills[i]) // 五块钱放后面 48 | // } 49 | // } 50 | 51 | // // 找零 52 | // function getMoney(money) { 53 | // let j = 0 // 找零指针 54 | // while (moneyArr.length > j) { 55 | // const nowMoney = moneyArr[j] 56 | // // 比客户给的少 代表可以找给客户 57 | // if (nowMoney <= money) { 58 | // money = money - nowMoney 59 | // moneyArr.splice(j, 1) // 找零后 去掉元素 60 | // } else { 61 | // j++ // 增加指针 62 | // } 63 | // // 找零成功 互不拖欠 64 | // if (money === 0) { 65 | // return true 66 | // } 67 | // } 68 | // return false // money不为0 找零失败 69 | // } 70 | // return true 71 | // }; 72 | -------------------------------------------------------------------------------- /src/leetCode/lexicographical-numbers/lexicographical-numbers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number[]} 4 | */ 5 | var lexicalOrder = function (n) { 6 | let result = [] 7 | // 从1开始 8 | for (let i = 1; i <= n; i++) { 9 | result.push(`${i}`) 10 | } 11 | result = result.sort() // sort按照字典序排列 12 | return result 13 | } 14 | 15 | // /** 树 16 | // * @param {number} n 17 | // * @return {number[]} 18 | // */ 19 | // var lexicalOrder = function (n) { 20 | // const stack = [9, 8, 7, 6, 5, 4, 3, 2, 1] // 顺序 21 | // const res = [] 22 | // let i = stack.length; let temp; let 23 | // front 24 | // while (i) { 25 | // while (i--) { 26 | // front = stack.pop() // 取出前缀 27 | // if (front === undefined || front > n) continue 28 | // res.push(front) // 添加前缀 29 | // for (let i = 9; i >= 0; i--) stack.push(+(`${front}${i}`)) // 该前缀的所有字段添加到末尾 下次就会被添加进字典序中 30 | // } 31 | // i = stack.length 32 | // } 33 | // return res 34 | // } 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/leetCode/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | // 快慢指针 9 | // 快指针先走K步 10 | // 两个指针一起走 快指针到达终点后 返回慢指针 11 | var getKthFromEnd = function (head, k) { 12 | if (!head) return head // 处理边界情况 空链表直接返回 13 | let first = head // 快指针 14 | let sencond = head // 慢指针 15 | // 快指针先走K步 16 | for (let i = 0; i < k; i++) { 17 | first = first.next 18 | } 19 | // 两个指针一起走 快指针到达终点后 返回慢指针 20 | while (first) { 21 | first = first.next 22 | sencond = sencond.next 23 | } 24 | return sencond 25 | } 26 | 27 | /** 28 | * 递归找层级 从末端开始算层级 29 | * 找到最后第k个层级 返回该链表 30 | */ 31 | // var getKthFromEnd = function (head, k) { 32 | // if (!head) return head // 处理边界情况 空链表直接返回 33 | // let help = (currList) => { 34 | // // 最后层级 35 | // if (currList == null) { 36 | // return 0 37 | // } 38 | // let res = help(currList.next) 39 | // // 判断是否找到层级 40 | // if (typeof res === 'number') { 41 | // // 未找到 增加层级 42 | // res += 1 43 | // } else { 44 | // // 找到层级 直接返回链表 45 | // return res 46 | // } 47 | // // 层级相等 找到倒数链表 返回链表 48 | // if (res === k) return currList 49 | // // 层级未找到 继续递归 50 | // if (res < k) return res 51 | // } 52 | // return help(head) 53 | // }; 54 | 55 | 56 | // 找到所有层级 根据数量 再遍历一遍 到指定位置停下 返回 57 | -------------------------------------------------------------------------------- /src/leetCode/lian-xu-zi-shu-zu-de-zui-da-he-lcof/lian-xu-zi-shu-zu-de-zui-da-he-lcof.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 动态规划 4 | let maxSubArray = function (nums) { 5 | let res = nums[0] 6 | for (let i = 1; i < nums.length; i++) { 7 | let beforeIsUse = Math.max(nums[i - 1], 0) // 是否使用之前的最大值 使用0则遗弃 8 | nums[i] += beforeIsUse // 当前值 + 之前值 等于目前最大和 9 | res = Math.max(nums[i], res) // 更新存储的最大和 10 | } 11 | return res 12 | } 13 | 14 | // /** 15 | // * 动态规划 16 | // * @param {number[]} nums 17 | // * @return {number} 18 | // */ 19 | // let maxSubArray = function (nums) { 20 | // let max = nums[0] 21 | // let nowValue = nums[0] // 之前子数组的和 22 | // for (let i = 1; i < nums.length; i++) { 23 | // // 之前子数组的和 与当前值比较 24 | // nowValue = Math.max(nums[i], nums[i] + nowValue) 25 | // // 之前存储的最大值 与当前子数组的最大值比较 26 | // max = Math.max(max, nowValue) 27 | // } 28 | // return max 29 | // } 30 | -------------------------------------------------------------------------------- /src/leetCode/linked-list-cycle/linked-list-cycle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | // 快慢指针 10 | // 快指针每次走两步 如果是环状 他们总会相遇 遇到即环状 11 | var hasCycle = function (head) { 12 | let fast = head 13 | let slow = head 14 | while (fast && slow) { 15 | // 快指针走两步 16 | fast = fast.next 17 | if (fast) { 18 | fast = fast.next 19 | } else { 20 | // 到末尾 说明不是环状 21 | return false 22 | } 23 | // 走一步 24 | slow = slow.next 25 | // 相同 为环状 26 | if (fast === slow) { 27 | return true 28 | } 29 | } 30 | // 如果它们为null 说明不是环状 31 | return false 32 | } 33 | 34 | // 哈希 以链表为key 如果有key 则为环 35 | // var hasCycle = function (head) { 36 | // let m = new Map() 37 | // let curr = head 38 | // while (curr) { 39 | // // 如果有key 则为环 40 | // if (m.get(curr)) { 41 | // return true 42 | // } 43 | // // 以链表为key 44 | // m.set(curr, 1) 45 | // curr = curr.next 46 | // } 47 | // return false 48 | // }; 49 | -------------------------------------------------------------------------------- /src/leetCode/longest-palindromic-substring/longest-palindromic-substring.js: -------------------------------------------------------------------------------- 1 | 2 | // 5. 最长回文子串 3 | 4 | 5 | // 中心扩散法 6 | // 两种情况 7 | // 一种是回文子串长度为奇数(如aba,中心是b) 8 | // 另一种回文子串长度为偶数(如abba,中心是b,b) 9 | 10 | // 循环遍历字符串 对取到的每个值 都假设他可能成为最后的中心进行判断 11 | /** 12 | * @param {string} s 13 | * @return {string} 14 | */ 15 | let longestPalindrome = function (s) { 16 | if (s.length < 2) { 17 | return s 18 | } 19 | let res = '' 20 | for (let i = 0; i < s.length; i++) { 21 | // 回文子串长度是奇数 22 | helper(i, i) 23 | // 回文子串长度是偶数 24 | helper(i, i + 1) 25 | } 26 | 27 | function helper(m, n) { 28 | // 从中心 扩散寻找相同字符 29 | // 相同的代表符合,继续扩散 查找相同的。 30 | while (m >= 0 && n < s.length && s[m] === s[n]) { 31 | m-- 32 | n++ 33 | } 34 | // 注意此处m,n的值循环完后 是恰好不满足循环条件的时刻 35 | // 此时m到n的距离为n-m+1,但是mn两个边界不能取 所以应该取m+1到n-1的区间 长度是n-m-1 36 | if (n - m - 1 > res.length) { 37 | // slice也要取[m+1,n-1]这个区间 38 | res = s.slice(m + 1, n) 39 | } 40 | } 41 | return res 42 | } 43 | 44 | 45 | 46 | 47 | // // 动态规划 48 | // let longestPalindrome = function (s) { 49 | // if (!s || s.length === 0) return '' 50 | // let res = s[0] 51 | 52 | // const dp = [] 53 | 54 | // // 倒着遍历简化操作, 这么做的原因是dp[i][..]依赖于dp[i + 1][..] 55 | // for (let i = s.length - 1; i >= 0; i--) { 56 | // dp[i] = [] 57 | // for (let j = i; j < s.length; j++) { 58 | // // specail case就是一个字符(轴对称点是本身),或者两个字符(轴对称点是介于两者之间的虚拟点) 59 | // if (j - i === 0) dp[i][j] = true 60 | // // specail case 1 61 | // else if (j - i === 1 && s[i] === s[j]) dp[i][j] = true 62 | // // specail case 2 63 | // else if (s[i] === s[j] && dp[i + 1][j - 1]) { // 这两个字符相等 并且轴对称 64 | // // state transition 65 | // dp[i][j] = true // 当前循环为回文 状态切成轴对称 66 | // } 67 | 68 | // if (dp[i][j] && j - i + 1 > res.length) { 69 | // // update res 70 | // res = s.slice(i, j + 1) 71 | // } 72 | // } 73 | // } 74 | 75 | // return res 76 | // } 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/leetCode/longest-substring-without-repeating-characters/longest-substring-without-repeating-characters.js: -------------------------------------------------------------------------------- 1 | // 双指针 2 | let lengthOfLongestSubstring = function (s) { 3 | // 哈希集合,记录每个字符是否出现过 4 | const occ = new Set() 5 | const n = s.length 6 | // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动 7 | let rk = -1; let 8 | ans = 0 9 | for (let i = 0; i < n; ++i) { 10 | if (i !== 0) { 11 | // 左指针向右移动一格,移除一个字符 12 | occ.delete(s.charAt(i - 1)) 13 | } 14 | while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) { 15 | // 不断地移动右指针 16 | occ.add(s.charAt(rk + 1)) 17 | ++rk 18 | } 19 | // 第 i 到 rk 个字符是一个极长的无重复字符子串 20 | ans = Math.max(ans, rk - i + 1) 21 | } 22 | return ans 23 | } 24 | 25 | // let lengthOfLongestSubstring = function (s) { 26 | // let i = 0 // 不重复字符的index 27 | // let res = 0 // 更新无重复字符的长度 28 | // for (let j = 0; j < s.length; j++) { 29 | // // 查找:不重复字符-当前index之间 有没有出现当前字符 30 | // let index = s.slice(i, j).indexOf(s[j]) 31 | // if (index === -1) { 32 | // // 更新无重复字符的长度:当前index-不重复字符的index + 长度从1开始算 33 | // res = Math.max(res, j - i + 1) 34 | // } else { 35 | // // 更新i = 不重复字符的index 36 | // // 不重复字符的index = 原不重复的字符index + i-j中出现重复字符的index + 跳过该重复字符 37 | // i = i + index + 1 38 | // } 39 | // } 40 | // return res 41 | // } 42 | 43 | 44 | 45 | 46 | // 哈希 47 | // let lengthOfLongestSubstring = function (s) { 48 | // let obj = {} // 用于储存字符出现的位置 49 | // let res = 0 // 最大值 50 | // let j = 0 // 不重复字符的index 51 | // for (let i = 0; i < s.length; i++) { 52 | // // 当前值是否在对象中存储过 53 | // const value = obj[s[i]] 54 | // if (value !== undefined) { 55 | // // 更新上一次重复值的index 56 | // // value + 1 跳过之前重复的字符 57 | // // j: 之前不重复的index 重复字符 需要全部跳过 58 | // j = Math.max(value + 1, j) 59 | // } 60 | // // 每个字符都计算一下最长不重复值 保存最大值 61 | // // 不重复最长长度 = 当前index - 上一次重复值的index + index从0开始 长度从1开始 62 | // res = Math.max(res, i - j + 1) 63 | // // 存/更新 字符串index 64 | // obj[s[i]] = i 65 | // } 66 | // return res 67 | // } 68 | -------------------------------------------------------------------------------- /src/leetCode/lowest-common-ancestor-of-a-binary-tree/lowest-common-ancestor-of-a-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @param {TreeNode} p 11 | * @param {TreeNode} q 12 | * @return {TreeNode} 13 | */ 14 | var lowestCommonAncestor = function (root, p, q) { 15 | if (!root) return null // 递归终止 16 | if (root === p || root === q) return root // 返回匹配到p和q的节点 17 | // 获取左右的祖先节点 18 | let left = lowestCommonAncestor(root.left, p, q) 19 | let right = lowestCommonAncestor(root.right, p, q) 20 | if (!left && !right) return null // 递归终止 21 | if (left && right) return root // 左右都匹配到了 本节点为最近祖先节点 22 | // 如果是儿子匹配到了 也只会返回一边 23 | if (left) return left // 左边匹配到了 返回左边 24 | if (right) return right // 右边匹配到了返回右边 25 | return root // 返回根节点 26 | } 27 | -------------------------------------------------------------------------------- /src/leetCode/lru-cache/lru-cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} capacity 3 | */ 4 | var LRUCache = function (capacity) { 5 | this.map = new Map() // map默认记住插入的顺序 6 | this.max = capacity // 最大数量 7 | } 8 | 9 | /** 10 | * @param {number} key 11 | * @return {number} 12 | */ 13 | LRUCache.prototype.get = function (key) { 14 | const value = this.map.get(key) || -1 15 | if (value !== -1) { 16 | this.map.delete(key) // 删除更新插入顺序 17 | this.map.set(key, value) 18 | } 19 | return value 20 | } 21 | /** 22 | * @param {number} key 23 | * @param {number} value 24 | * @return {void} 25 | */ 26 | LRUCache.prototype.put = function (key, value) { 27 | if (this.map.has(key)) { 28 | this.map.delete(key) // 删除更新插入顺序 29 | } 30 | this.map.set(key, value) 31 | if (this.max < this.map.size) { 32 | const mapKeys = this.map.keys() // 获取遍历值 33 | const oldKey = mapKeys.next().value // map插入顺序 默认第一个即最早插入的值 34 | this.map.delete(oldKey) // 删除最早的值 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/leetCode/majority-element/majority-element.js: -------------------------------------------------------------------------------- 1 | // 1. 常规解法 遍历用映射表计算数量 2 | 3 | /** 4 | * 2. 排序 因多数元素超过一半 中间数 即为那个值 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | // var majorityElement = function (nums) { 9 | // nums.sort((a, b) => a - b) 10 | // let mid = Math.floor(nums.length / 2) 11 | // return nums[mid] 12 | // }; 13 | 14 | // 投票算法 15 | // 开心消消乐 互相碰撞 16 | var majorityElement = function (nums) { 17 | let count = 0 // 最多数的数量 18 | let moreNumber = null // 最多的数 19 | for (let i = 0; i < nums.length; i++) { 20 | // 更新最大数 21 | if (count === 0) { 22 | moreNumber = nums[i] 23 | } 24 | // 碰撞 更新最大数的数量 25 | if (moreNumber === nums[i]) { 26 | count++ 27 | } else { 28 | count-- 29 | } 30 | } 31 | return moreNumber 32 | } 33 | -------------------------------------------------------------------------------- /src/leetCode/matrix-cells-in-distance-order/matrix-cells-in-distance-order.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} R 3 | * @param {number} C 4 | * @param {number} r0 5 | * @param {number} c0 6 | * @return {number[][]} 7 | */ 8 | // 两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2| 9 | 10 | // 桶排序 11 | // 求出矩阵每个点到 (r0, c0) 的曼哈顿距离。 12 | // 既然要按照距离大小排,那就把相同距离的坐标丢到一个数组里(桶),用一个map管理。 13 | // 然后按距离从小到大遍历这些桶,把桶里的坐标,逐个加入结果数组。 14 | var allCellsDistOrder = function (R, C, r0, c0) { 15 | let res = [] // 结果 16 | let hash = {} // 桶 17 | for (let i = 0; i < R; i++) { 18 | for (let j = 0; j < C; j++) { 19 | // 同一距离放进去 20 | const d = getD(i, j, r0, c0) 21 | if (!hash[d]) { 22 | hash[d] = [[i, j]] 23 | } else { 24 | hash[d].push([i, j]) 25 | } 26 | } 27 | } 28 | // 最远距离行+列 -1 = R + C - 1 29 | for (let d = 0; d <= R + C - 1; d++) { 30 | if (!hash[d]) continue // 没有这个桶 跳过 31 | for (const pair of hash[d]) { 32 | res.push(pair) 33 | } 34 | } 35 | return res 36 | } 37 | 38 | // 计算曼哈顿距离 39 | var getD = (x1, y1, x2, y2) => { 40 | return Math.abs(x1 - x2) + Math.abs(y1 - y2) 41 | } 42 | 43 | // var allCellsDistOrder = (R, C, r0, c0) => { 44 | // const res = []; 45 | // const hash = {}; 46 | 47 | // for (let i = 0; i < R; i++) { 48 | // for (let j = 0; j < C; j++) { 49 | // const d = getD(i, j, r0, c0); 50 | // if (!hash[d]) { 51 | // hash[d] = [[i, j]]; 52 | // } else { 53 | // hash[d].push([i, j]); 54 | // } 55 | // } 56 | // } 57 | // for (let d = 0; d <= R + C - 2; d++) { 58 | // if (!hash[d]) continue; 59 | // for (const pair of hash[d]) { 60 | // res.push(pair); 61 | // } 62 | // } 63 | // return res; 64 | // }; 65 | 66 | // var getD = (x1, y1, x2, y2) => { 67 | // return Math.abs(x1 - x2) + Math.abs(y1 - y2); 68 | // }; 69 | -------------------------------------------------------------------------------- /src/leetCode/max-area-of-island/max-area-of-island.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} grid 3 | * @return {number} 4 | */ 5 | var maxAreaOfIsland = function (grid) { 6 | let len1 = grid.length 7 | let len2 = grid[0].length 8 | let max = 0 9 | for (let i = 0; i < len1; i++) { 10 | for (let j = 0; j < len2; j++) { 11 | if (grid[i][j] === 1) { 12 | // 获取这片岛屿的面积 13 | let res = dfs(i, j) 14 | // 更新最大面积 15 | max = Math.max(max, res) 16 | } 17 | } 18 | } 19 | // 深度遍历 20 | function dfs(i, j) { 21 | let init = 1 22 | let res1 = 0 23 | let res2 = 0 24 | let res3 = 0 25 | let res4 = 0 26 | grid[i][j] = 0 27 | // 上 28 | if (i > 0 && grid[i - 1][j] === 1) { 29 | res1 = dfs(i - 1, j) 30 | } 31 | // 下 32 | if (i < len1 - 1 && grid[i + 1][j] === 1) { 33 | res2 = dfs(i + 1, j) 34 | } 35 | // 左 36 | if (j > 0 && grid[i][j - 1] === 1) { 37 | res3 = dfs(i, j - 1) 38 | } 39 | // 右 40 | if (j < len2 - 1 && grid[i][j + 1] === 1) { 41 | res4 = dfs(i, j + 1) 42 | } 43 | // 获取每个方向有几个1 相加即为面积 44 | return init + res1 + res2 + res3 + res4 45 | } 46 | return max 47 | } 48 | -------------------------------------------------------------------------------- /src/leetCode/maximum-depth-of-binary-tree/maximum-depth-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | 10 | // 广度优先 11 | var maxDepth = function (root) { 12 | if (root === null) return 0 13 | let stack = [root] // 初始化层级 14 | let max = 0 15 | while (stack.length) { 16 | let len = stack.length 17 | // 在栈中删除所有当前层级节点 添加所有当前层级节点的子节点 18 | for (let i = 0; i < len; i++) { 19 | let item = stack.shift() 20 | if (item.left !== null) stack.push(item.left) 21 | if (item.right !== null) stack.push(item.right) 22 | } 23 | max++ 24 | } 25 | return max 26 | } 27 | /** 28 | * 深度优先 29 | * @param {TreeNode} root 30 | * @return {number} 31 | */ 32 | // var maxDepth = function (root) { 33 | // if (root === null) { 34 | // return 0 35 | // } else { 36 | // let left = maxDepth(root.left) 37 | // let right = maxDepth(root.right) 38 | // // 最后一层是0 返回一个数字 不断加1 39 | // return Math.max(left, right) + 1 40 | // } 41 | // }; 42 | -------------------------------------------------------------------------------- /src/leetCode/maximum-depth-of-n-ary-tree/maximum-depth-of-n-ary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * // Definition for a Node. 3 | * function Node(val,children) { 4 | * this.val = val; 5 | * this.children = children; 6 | * }; 7 | */ 8 | 9 | /** 10 | * @param {Node} root 11 | * @return {number} 12 | */ 13 | var maxDepth = function (root) { 14 | let max = 0 15 | const helpFn = (node, high) => { 16 | if (max < high) { 17 | max = high 18 | } 19 | // 没有叶子节点 20 | if (node.children.length === 0) return 21 | // 递归子节点 找到最深高度 22 | for (let i = 0; i < node.children.length; i++) { 23 | helpFn(node.children[i], high + 1) 24 | } 25 | } 26 | if (root) { 27 | helpFn(root, 1) 28 | } 29 | return max 30 | } 31 | -------------------------------------------------------------------------------- /src/leetCode/maximum-subarray/maximum-subarray.js: -------------------------------------------------------------------------------- 1 | 2 | // 53. 最大子数组和 3 | 4 | // 动态规划 5 | /** 6 | * @param {number[]} nums 7 | * @return {number} 8 | */ 9 | let maxSubArray = function (nums) { 10 | // 存储最大的值 11 | let dp = [nums[0]] 12 | let res = dp[0] 13 | for (let i = 1; i < nums.length; i++) { 14 | if (dp[i - 1] > 0) { 15 | dp[i] = dp[i - 1] + nums[i] 16 | } else { 17 | dp[i] = nums[i] 18 | } 19 | res = Math.max(res, dp[i]) 20 | } 21 | return res 22 | } 23 | 24 | 25 | 26 | // /** 27 | // * @param {number[]} nums 28 | // * @return {number} 29 | // */ 30 | // let maxSubArray = function (nums) { 31 | // // 存储最大的值 32 | // let max = nums[0] 33 | // let before = nums[0] 34 | // for (let i = 1; i < nums.length; i++) { 35 | // const now = nums[i] 36 | // // 更新当前最大值: 下一个值与之前值相加 取大的 37 | // if (now >= before + now) { 38 | // before = now 39 | // } else { 40 | // before += now 41 | // } 42 | // // 更新最大值 43 | // if (before > max) { 44 | // max = before 45 | // } 46 | // } 47 | // return max 48 | // } 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/leetCode/maximum-swap/maximum-swap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 排序 找到这串字符串的每个最大值 3 | * 只要前面的最大值不与num相同 即证明有优化空间 4 | * 不相同的话 从后面开始找 先找到的最大值 就是最小的 5 | */ 6 | var maximumSwap = function (num) { 7 | const str = String(num) 8 | const numArr = str.split('') 9 | let res = str.split('') 10 | // 排序 11 | res.sort((a, b) => b - a) 12 | for (let i = 0; i < str.length; i++) { 13 | // 最大值与字符串不相等 14 | if (str[i] !== res[i]) { 15 | // 找到最大值的位置交换目前不等于最大值的数字 16 | // 最大值的位置从后面开始找 提换后比较大 17 | numArr.splice(i, 1, Number(res[i])) // 把左侧小于最大值的数字替换成最大值 18 | let index = str.lastIndexOf(res[i]) 19 | numArr.splice(index, 1, Number(str[i])) // 把当前小于最小值的数字替换最大值 20 | return Number(numArr.join('')) 21 | } 22 | } 23 | return num 24 | } 25 | 26 | // /** 27 | // * 1. 记录每个数字位置最后出现的地方 28 | // * 2. 找到比当前字符大的数字字符 29 | // * 3. 字符出现的位置比当前字符串靠后 30 | // * 4. 交换 31 | // * @param {number} num 32 | // * @return {number} 33 | // */ 34 | // var maximumSwap = function (num) { 35 | // // 记录每个数字位置最后出现的地方 36 | // let hash = { 37 | // 0: -1, 38 | // 1: -1, 39 | // 2: -1, 40 | // 3: -1, 41 | // 4: -1, 42 | // 5: -1, 43 | // 6: -1, 44 | // 7: -1, 45 | // 8: -1, 46 | // 9: -1, 47 | // } 48 | // // 转数组 49 | // let numArr = Array.from(num.toString()) 50 | // for (let i = 0; i < numArr.length; i++) { 51 | // hash[Number(numArr[i])] = i 52 | // } 53 | // for (let i = 0; i < numArr.length; i++) { 54 | // let num1 = Number(numArr[i]) // 当前字符位置 55 | // // 从大开始循环 找到比大的小的字符 56 | // for (let d = 9; d > num1; d--) { 57 | // let index = hash[d] 58 | // if (index > i) { 59 | // // 字符出现的位置比当前字符串靠后交换 60 | // let temp = numArr[index] // 大值 61 | // numArr[index] = numArr[i] // 小值 62 | // numArr[i] = temp 63 | // return Number(numArr.join('')) 64 | // } 65 | // } 66 | // } 67 | // return num 68 | // } 69 | -------------------------------------------------------------------------------- /src/leetCode/meeting-rooms-ii/meeting-rooms-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | 看了题解以后,觉得自己很SB 3 | 使用上车下车算法: 4 | 1. 分别对开始时间和结束时间排序。 5 | 2. 排序以后用两指针分别迭代两个数组。 使用计数器表示上车和下车的人数,start小的计数器+1,end小的计数器-1,分别表示上车数和下车人数。如果end和start的值一样,说明正好抵消,有人上车的同时有人下车。 6 | 3. 需要记录车上某个时刻的最大值, 这个最大值就是需要的会议室的数量。 7 | * */ 8 | var minMeetingRooms = function (intervals) { 9 | if (intervals.length === 0) return 0 10 | let start = [] 11 | let end = [] 12 | 13 | // 排序 14 | for (let i = 0; i < intervals.length; i++) { 15 | start.push(intervals[i][0]) 16 | end.push(intervals[i][1]) 17 | } 18 | start.sort((a, b) => a - b) 19 | end.sort((a, b) => a - b) 20 | 21 | // 上车下车 22 | let p1 = 0; let 23 | p2 = 0 // 指针 24 | let max = 0 // 记录车上的人数最大值 25 | let count = 0 // 车上人数 26 | // p1 是start的指针,p1到最后也就结束了 27 | while (p1 < start.length) { 28 | if (start[p1] < end[p2]) { 29 | // 有人上车 30 | count++ 31 | p1++ 32 | // 记录车上的人数最大值 33 | max = Math.max(max, count) 34 | } else if (start[p1] > end[p2]) { 35 | // 有人下车 36 | count-- 37 | p2++ 38 | } else { 39 | // 时间重合 同时有人上车与下车 40 | // 开始: count++ 41 | // 结束会议: count-- 42 | p1++ 43 | p2++ 44 | } 45 | } 46 | return max 47 | } 48 | -------------------------------------------------------------------------------- /src/leetCode/merge-intervals/merge-intervals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} intervals 3 | * @return {number[][]} 4 | */ 5 | var merge = function (intervals) { 6 | if (intervals.length === 0) return [] 7 | let res = [] 8 | // 排序每个数组元素的第一个元素 9 | // 排序后第一个元素默认等于大于 区间的第一个元素 不用处理 只处理第二区间 10 | intervals.sort((a, b) => { 11 | return a[0] - b[0] 12 | }) 13 | // 初始化区间 从二维数组的第二个元素开始比较 14 | res.push(intervals[0]) 15 | for (let i = 1; i < intervals.length; i++) { 16 | if (intervals[i][0] > res[res.length - 1][1]) { 17 | // 当前数组元素的第一个元素 比之前区间的比较大的元素大的话 即为新区间 18 | res.push(intervals[i]) 19 | } else if (intervals[i][1] > res[res.length - 1][1]) { 20 | // 当前数组元素的最大值 大于区间中的最大值即为重合 21 | // 更新前一个元素的最大值 为第二个值 22 | res[res.length - 1][1] = intervals[i][1] 23 | } 24 | // 数组第二个元素 小于等于当前区间不处理 25 | } 26 | return res 27 | } 28 | -------------------------------------------------------------------------------- /src/leetCode/merge-sorted-array/merge-sorted-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 双指针 3 | * 插入位置有一个对应的指针 4 | * 两个数组对应元素相比较 5 | * 大的元素 插到大数组的指针上 并且减去一个指针 6 | * @param {number[]} nums1 7 | * @param {number} m 8 | * @param {number[]} nums2 9 | * @param {number} n 10 | * @return {void} Do not return anything, modify nums1 in-place instead. 11 | */ 12 | var merge = function (nums1, m, nums2, n) { 13 | let len1 = m - 1 // 第一个数组元素比较的指针 14 | let len2 = n - 1 // 第二个数组元素比较的指针 15 | let site = m + n - 1 // 插入位置的指针 16 | // 两个数组的指针都要大于0 17 | while (len1 >= 0 || len2 >= 0) { 18 | // 比较大小:nums2的元素插入到指针位置 并且移动nums2比较的指针 19 | // 边界情况:len1为空 len2 指针还未完成 如果nums1[len1] === undefined 说明将 nums2[len2]直接插入即可 如果nums2[len2]是undefind 则进入else 添加nums1[len1] 20 | if (nums1[len1] < nums2[len2] || (nums1[len1] === undefined && len2 >= 0)) { 21 | nums1[site] = nums2[len2] 22 | len2-- 23 | } else { 24 | // nums1 元素比较大 插入 移动nums1比较的指针 25 | nums1[site] = nums1[len1] 26 | len1-- 27 | } 28 | site-- // 插入位置 指针减一 29 | } 30 | return nums1 31 | } 32 | -------------------------------------------------------------------------------- /src/leetCode/merge-two-binary-trees/merge-two-binary-trees.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | 11 | // 递归 12 | // var mergeTrees = function (root1, root2) { 13 | // // 两边都有节点 合成 14 | // if (root1 && root2) { 15 | // let node = new TreeNode() 16 | // // 递归 17 | // node.left = mergeTrees(root1.left, root2.left) 18 | // node.right = mergeTrees(root1.right, root2.right) 19 | // node.val = root1.val + root2.val 20 | // return node 21 | // } else { 22 | // // 只有一个节点 直接返回 23 | // return root1 || root2 24 | // } 25 | // }; 26 | 27 | // 广度优先 28 | var mergeTrees = function (root1, root2) { 29 | if (root2 === null) return root1 30 | if (root1 === null) return root2 31 | let tree = new TreeNode(root1.val + root2.val) 32 | // 三个栈 同进同出 33 | let queen = [tree] 34 | let queen1 = [root1] 35 | let queen2 = [root2] 36 | while (queen1.length && queen2.length) { 37 | let node = queen.shift() 38 | let t1 = queen1.shift() 39 | let t2 = queen2.shift() 40 | if (t1.left !== null && t2.left !== null) { 41 | node.left = new TreeNode(t1.left.val + t2.left.val) 42 | // 添加左子树 43 | queen1.push(t1.left) 44 | queen2.push(t2.left) 45 | queen.push(node.left) 46 | } else if (t1.left !== null) { 47 | node.left = t1.left 48 | } else if (t2.left !== null) { 49 | node.left = t2.left 50 | } 51 | if (t1.right !== null && t2.right !== null) { 52 | node.right = new TreeNode(t1.right.val + t2.right.val) 53 | // 添加右子树 54 | queen1.push(t1.right) 55 | queen2.push(t2.right) 56 | queen.push(node.right) 57 | } else if (t1.right !== null) { 58 | node.right = t1.right 59 | } else if (t2.right !== null) { 60 | node.right = t2.right 61 | } 62 | } 63 | return tree 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/leetCode/merge-two-sorted-lists/merge-two-sorted-lists.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val, next) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.next = (next===undefined ? null : next) 7 | * } 8 | */ 9 | /** 10 | * @param {ListNode} l1 11 | * @param {ListNode} l2 12 | * @return {ListNode} 13 | */ 14 | /** 15 | * 递归组建一条链表 16 | * 1. 把问题变成子问题:递归的最小值 17 | * 2. 比较链表的值 生成一个新链表 通过递归连接起来 18 | * 3. 在结束条件直接返回链表的空值 19 | * 4. 归纳: 回溯 链接起来 20 | */ 21 | var mergeTwoLists = function (l1, l2) { 22 | let newL = new ListNode(undefined, undefined) 23 | if (!l1) { 24 | // l1没有 直接返回l2 链接起来 25 | return l2 26 | } if (!l2) { 27 | // l2没有 直接返回l1 链接起来 28 | return l1 29 | } 30 | // 两个链表都有值 先添加一个小值 剩下一个继续递归生成next链表 这是排序的过程 31 | if (l1.val >= l2.val) { 32 | // l2比较小 先添加 33 | newL.val = l2.val 34 | newL.next = mergeTwoLists(l1, l2.next) 35 | } else { 36 | newL.val = l1.val 37 | newL.next = mergeTwoLists(l1.next, l2) 38 | } 39 | 40 | return newL 41 | } 42 | 43 | // // 循环 拼接链表 44 | // let mergeTwoLists = function (l1, l2) { 45 | // let newL = new ListNode() 46 | // let curr = newL 47 | // while (l1 || l2) { 48 | // if (!l1) { 49 | // // 一条链表没有了 直接返回另一条 链接起来 50 | // curr.next = l2 51 | // l2 = null 52 | // } else if (!l2) { 53 | // // 一条链表没有了 直接返回另一条 链接起来 54 | // curr.next = l1 55 | // l1 = null 56 | // } else { 57 | // // 根据指针 拼接节点 58 | // if (l1.val > l2.val) { 59 | // curr.next = l2 60 | // l2 = l2.next 61 | // } else { 62 | // curr.next = l1 63 | // l1 = l1.next 64 | // } 65 | // // 移动指针 66 | // curr = curr.next 67 | // } 68 | // } 69 | // return newL.next 70 | // } 71 | -------------------------------------------------------------------------------- /src/leetCode/middle-of-the-linked-list/middle-of-the-linked-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | // 快慢指针 优点: 只用遍历一半长度 10 | // 快指针走两步 慢指针走一步 11 | var middleNode = function (head) { 12 | let fast = head 13 | let slow = head 14 | while (fast && fast.next) { 15 | slow = slow.next 16 | fast = fast.next.next 17 | } 18 | return slow 19 | } 20 | 21 | // 数组 获取所有节点 除以2 返回中间节点 22 | -------------------------------------------------------------------------------- /src/leetCode/min-cost-climbing-stairs/min-cost-climbing-stairs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态规划 3 | * @param {number[]} cost 4 | * @return {number} 5 | */ 6 | let minCostClimbingStairs = function (cost) { 7 | let n = cost.length 8 | let dp = new Array(n + 1).fill(0) // 爬最后一层免费 多取最后一层 9 | // 前两个台阶免费 10 | dp[0] = 0 11 | dp[1] = 0 12 | for (let i = 2; i <= n; i++) { 13 | // 可以从下标 i-1i−1 使用 \textit{cost}[i-1]cost[i−1] 的花费达到下标 ii,或者从下标 i-2i−2 使用 \textit{cost}[i-2]cost[i−2] 的花费达到下标 ii。为了使总花费最小,\textit{dp}[i]dp[i] 应取上述两项的最小值 14 | dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]) 15 | } 16 | return dp[n] 17 | } 18 | 19 | // // 空间优化:使用变量保存 20 | 21 | // var minCostClimbingStairs = function(cost) { 22 | // const n = cost.length; 23 | // let prev = 0, curr = 0; 24 | // for (let i = 2; i <= n; i++) { 25 | // let next = Math.min(curr + cost[i - 1], prev + cost[i - 2]); 26 | // prev = curr; 27 | // curr = next; 28 | // } 29 | // return curr; 30 | // }; 31 | -------------------------------------------------------------------------------- /src/leetCode/minimum-depth-of-binary-tree/minimum-depth-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | 10 | 11 | 12 | /** 13 | * 深度优先 14 | * @param {TreeNode} root 15 | * @return {number} 16 | */ 17 | // var minDepth = function (root) { 18 | // let min = 0 // 初始化 19 | // const countHigh = (node, res) => { 20 | // if (node === null) return // 递归终止 21 | // // 叶子节点 22 | // if (node.left === null && node.right === null) { 23 | // // 最小高度 比 当前高度高 更新最小高度 24 | // if (min > res || min === 0) { 25 | // min = res 26 | // } 27 | // return // 已经有叶子节点退出 第一个 28 | // } 29 | // countHigh(node.left, res + 1) 30 | // countHigh(node.right, res + 1) 31 | // } 32 | // countHigh(root, 1) 33 | // return min 34 | // }; 35 | 36 | 37 | // 广度优先 38 | var minDepth = function (root) { 39 | if (root === null) return 0 40 | let stack = [root] 41 | let count = 0 // 当前节点 42 | while (stack.length) { 43 | let len = stack.length 44 | count++ // 当前层级高度+1 45 | // 循环清空每个层级 46 | for (let i = 0; i < len; i++) { 47 | let node = stack.shift() 48 | // 广度排序 第一个叶子节点即为最小深度 49 | if (node.left === null && node.right === null) { 50 | return count 51 | } 52 | // 添加节点 53 | if (node.left) { 54 | stack.push(node.left) 55 | } 56 | if (node.right) { 57 | stack.push(node.right) 58 | } 59 | } 60 | } 61 | return count 62 | } 63 | -------------------------------------------------------------------------------- /src/leetCode/minimum-path-sum/minimum-path-sum.js: -------------------------------------------------------------------------------- 1 | // 动态规划 2 | let minPathSum = function (grid) { 3 | const m = grid.length // 二维长度 4 | const n = grid[0].length // 二维宽度 5 | // 倒序遍历 6 | for (let i = m - 1; i >= 0; i--) { 7 | for (let j = n - 1; j >= 0; j--) { 8 | // 计算每一步移动到终点位置的代价 得到最终代价 9 | if (i + 1 < m && j + 1 < n) { 10 | // 可以向左和向上移动的情况 取最小值 动规 11 | grid[i][j] += Math.min(grid[i + 1][j], grid[i][j + 1]) 12 | } else if (i + 1 < m) { 13 | // 只能向上移动的情况 14 | // 加上下一行数组的最后一个元素 计算向上移动的代价 15 | grid[i][j] += grid[i + 1][j] 16 | } else if (j + 1 < n) { 17 | // 只能向左移动的情况 18 | // 加上右侧的原先的值 计算向左移动的代价 19 | grid[i][j] += grid[i][j + 1] 20 | } 21 | } 22 | } 23 | return grid[0][0] // 由起点到终点的位置 24 | } 25 | 26 | 27 | // 深度优先 28 | // var minPathSum = function (grid) { 29 | // const m = grid.length, 30 | // n = grid[0].length; 31 | // const memo = [...Array(m)].map(() => [...Array(n)]); 32 | // const calcPath = function (i, j) { 33 | // if (i + 1 === m && j + 1 === n) { 34 | // return grid[i][j]; // 返回终点位置的值 35 | // } 36 | // if (memo[i][j]) { 37 | // return memo[i][j]; // 计算过了, 不重复计算 38 | // } 39 | // let min = Infinity; // 初始化最大值 40 | // // 计算往右走以及往下走的步数,取最小值 41 | // if (i + 1 < m) { 42 | // // 防止右侧出界 43 | // // 计算往右走的最终代价 44 | // min = Math.min(min, calcPath(i + 1, j)); 45 | // } 46 | // // 往下走的步数 47 | // if (j + 1 < n) { 48 | // // 防止下方出界 49 | // // 计算往下走的最终代价 50 | // min = Math.min(min, calcPath(i, j + 1)); 51 | // } 52 | // memo[i][j] = min + grid[i][j]; // 终点到当前位置的代价 53 | // return memo[i][j]; // 返回每个位置到终点位置的代价 54 | // }; 55 | // let res = calcPath(0, 0); 56 | // return res; 57 | // }; 58 | -------------------------------------------------------------------------------- /src/leetCode/minimum-remove-to-make-valid-parentheses/minimum-remove-to-make-valid-parentheses.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {string} 4 | */ 5 | // 栈元素保存下标 以及括号方向 6 | // 赚数组 删除留下undefined 最后再拼接 7 | // 添加所有的左括号 8 | // 右括号 如果上一个元素不是左括号的话 代表不合法 就置空 9 | // 如果是的话 就括号对撞 删除左括号 10 | // 最后留在栈里面的括号 就是要删除的左括号 11 | // 因为左括号会一直加入,到最后可能会有剩余,循环删除栈中 剩余的左括号 12 | // 去掉数组中所有undefined(被删除)的元素 13 | // 转字符串 输出 14 | var minRemoveToMakeValid = function (s) { 15 | let arr = s.split('') 16 | let stack = [] // 储存括号的下标 方便后期删除数组中的左括号 17 | for (let i = 0; i < arr.length; i++) { 18 | let item = arr[i] 19 | if (item === '(') { 20 | // 添加所有的左括号 21 | stack.push(i) 22 | } else if (item === ')') { 23 | let index = stack[stack.length - 1] 24 | let stackItem = s[index] 25 | // 右括号 如果上一个元素不是左括号的话 代表不合法 就置空 26 | if (stackItem !== '(') { 27 | arr[i] = undefined 28 | } else { 29 | // 如果是的话 就括号对撞 删除左括号 30 | stack.pop() 31 | } 32 | } 33 | } 34 | // 最后留在栈里面的括号 就是要删除的左括号 35 | // 因为左括号会一直加入,到最后可能会有剩余,循环删除栈中 剩余的左括号 36 | for (let i = 0; i < stack.length; i++) { 37 | let arrIndex = stack[i] 38 | arr[arrIndex] = undefined 39 | } 40 | // 去掉undefined(被删除)的元素 41 | const newArr = arr.filter((item) => item) 42 | return newArr.join('') // 转数组 43 | } 44 | -------------------------------------------------------------------------------- /src/leetCode/minimum-subsequence-in-non-increasing-order/minimum-subsequence-in-non-increasing-order.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[]} 4 | */ 5 | var minSubsequence = function (nums) { 6 | nums.sort((a, b) => b - a) // 排序 贪心:从大的开始加 7 | let res = [] 8 | let count = 0 9 | // 元素和 10 | let total = nums.reduce((prev, current) => { 11 | return prev + current 12 | }, 0) 13 | for (let i = 0; i < nums.length; i++) { 14 | res.push(nums[i]) 15 | count += nums[i] // 添加的元素和 16 | // 元素和大于 总和减去元素和 17 | if (count > total - count) { 18 | return res 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/leetCode/next-greater-element-i/next-greater-element-i.js: -------------------------------------------------------------------------------- 1 | // 单调栈解法 2 | // 哈希表 储存nums2下一个比它大的值 3 | var nextGreaterElement = function (nums1, nums2) { 4 | let stack = [] 5 | let hash = new Map() // 哈希表 储存nums2下一个比它大的值 6 | for (let i = 0; i < nums2.length; i++) { 7 | // 栈中如果有元素 找到比栈中最后一个元素大 8 | while (stack.length && stack[stack.length - 1] < nums2[i]) { 9 | hash.set(stack[stack.length - 1], nums2[i]) // 映射这个值的 下一个比它大的值 10 | stack.pop() // 找到比它大 则在栈中删除 11 | } 12 | stack.push(nums2[i]) // 入栈 找它的下一个比它大的元素 13 | } 14 | // 找nums1中的元素是否在hash中有值 有值就赋值 没值则-1 15 | for (let i = 0; i < nums1.length; i++) { 16 | let value = hash.get(nums1[i]) 17 | if (value !== undefined) { 18 | nums1[i] = value 19 | } else { 20 | nums1[i] = -1 21 | } 22 | } 23 | return nums1 24 | } 25 | 26 | /** 27 | * 暴力解法 28 | * @param {number[]} nums1 29 | * @param {number[]} nums2 30 | * @return {number[]} 31 | */ 32 | // var nextGreaterElement = function (nums1, nums2) { 33 | // for (let i = 0; i < nums1.length; i++) { 34 | // let target = nums1[i] 35 | // let find = false // 找到 36 | // let maxNum // 比它大的值 37 | // for (let j = 0; j < nums2.length; j++) { 38 | // if (nums2[j] === target) { 39 | // find = true 40 | // continue 41 | // } 42 | // // 找到之后 开始找后面有没有比它大的 43 | // if (find && nums2[j] > target) { 44 | // maxNum = nums2[j] // 找到 45 | // break 46 | // } 47 | // } 48 | // // 判断有没有找到 赋值 49 | // if (maxNum !== undefined) { 50 | // nums1[i] = maxNum 51 | // } else { 52 | // nums1[i] = -1 53 | // } 54 | // } 55 | // return nums1 56 | // }; 57 | -------------------------------------------------------------------------------- /src/leetCode/next-greater-element-ii/next-greater-element-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 单调栈 3 | * https://leetcode-cn.com/problems/next-greater-element-ii/solution/dong-hua-jiang-jie-dan-diao-zhan-by-fuxu-4z2g/ 4 | */ 5 | var nextGreaterElements = function (nums) { 6 | let stack = [] // 储存nums中的下标 方便查找nums中的值 以及 设置结果对应下标的下一个大值 7 | // 初始值-1 没有找到比它大的值 即不操作 -1 8 | let res = new Array(nums.length).fill(-1) 9 | let len = nums.length 10 | // 循环双倍长度 模拟数组循环 11 | for (let i = 0; i < len * 2; i++) { 12 | let index = i % len // 对双倍长度 取余求出nums中的下标 13 | let item = nums[index] // nums中循环值 14 | // 如果循环值比栈中最后一位大的话 则说明当前循环值为栈中值的下一个更大值 15 | while (stack.length && nums[stack[stack.length - 1]] < item) { 16 | let resIndex = stack.pop() // 取出栈中的下标 17 | res[resIndex] = item // 记录当前循环值为对应结果中的最大值 18 | // 继续取出栈中的值 比对循环值 19 | } 20 | // 入栈 寻找当前值的下一个最大值 21 | stack.push(index) 22 | } 23 | return res 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/next-permutation/next-permutation.js: -------------------------------------------------------------------------------- 1 | // “下一个排列” 的定义是:给定数字序列的字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 2 | // 我们可以将该问题形式化地描述为: 给定若干个数字,将其组合为一个整数。如何将这些数字重新排列,以得到下一个更大的整数。如 123 下一个更大的数为 132。如果没有更大的整数,则输出最小的整数。 3 | 4 | // https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-suan-fa-xiang-jie-si-lu-tui-dao-/ 5 | // 将后面的「大数」与前面的「小数」交换 6 | 7 | /** 8 | * @param {number[]} nums 9 | * @return {void} Do not return anything, modify nums in-place instead. 10 | */ 11 | var nextPermutation = function (nums) { 12 | if (nums.length <= 2) return nums.reverse() 13 | let i = nums.length - 2 14 | // 找到第一个右侧数 大于 左侧数的 下标 i (这样就代表当前组成的数字不是最大的) 15 | while (i >= 0 && nums[i + 1] <= nums[i]) { 16 | i-- 17 | } 18 | // 不是最后一个排列 19 | if (i >= 0) { 20 | let j = nums.length - 1 21 | // 右到左开始找 第一个大于 i下标数字的 下标 j 最小数 22 | while (j > 0 && nums[j] <= nums[i]) { 23 | j-- 24 | } 25 | // 找到后就是下一个排列 交换位置 26 | [nums[i], nums[j]] = [nums[j], nums[i]] 27 | } 28 | const reverseArr = (start, end) => { 29 | while (start < end) { 30 | [nums[start], nums[end]] = [nums[end], nums[start]] 31 | start++ 32 | end-- 33 | } 34 | } 35 | // 将i下标往后的 数组 进行反序,变成升序; 36 | // 因为前面i往后的数字都是从右往前找 都是一个比一个小的 是降序的 37 | reverseArr(i + 1, nums.length - 1) 38 | return nums 39 | } 40 | -------------------------------------------------------------------------------- /src/leetCode/pacific-atlantic-water-flow/pacific-atlantic-water-flow.js: -------------------------------------------------------------------------------- 1 | // 沿着海岸线逆流而上 2 | var pacificAtlantic = function (matrix) { 3 | if (!matrix || !matrix[0]) { return [] } 4 | const m = matrix.length 5 | const n = matrix[0].length 6 | const flow1 = Array.from({ length: m }, () => new Array(n).fill(false)) 7 | const flow2 = Array.from({ length: m }, () => new Array(n).fill(false)) 8 | const dfs = (r, c, flow) => { 9 | flow[r][c] = true; 10 | [[r - 1, c], [r + 1, c], [r, c - 1], [r, c + 1]].forEach(([nr, nc]) => { 11 | if ( 12 | // 保证在矩阵中 13 | nr >= 0 && nr < m 14 | && nc >= 0 && nc < n 15 | // 防止死循环 16 | && !flow[nr][nc] 17 | // 保证逆流而上 18 | && matrix[nr][nc] >= matrix[r][c] 19 | ) { 20 | dfs(nr, nc, flow) 21 | } 22 | }) 23 | } 24 | // 沿着海岸线逆流而上 25 | for (let r = 0; r < m; r += 1) { 26 | dfs(r, 0, flow1) 27 | dfs(r, n - 1, flow2) 28 | } 29 | for (let c = 0; c < n; c += 1) { 30 | dfs(0, c, flow1) 31 | dfs(m - 1, c, flow2) 32 | } 33 | // 收集能流到两个大洋里的坐标 34 | const res = [] 35 | for (let r = 0; r < m; r += 1) { 36 | for (let c = 0; c < n; c += 1) { 37 | if (flow1[r][c] && flow2[r][c]) { 38 | res.push([r, c]) 39 | } 40 | } 41 | } 42 | return res 43 | } 44 | -------------------------------------------------------------------------------- /src/leetCode/palindrome-linked-list/palindrome-linked-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | // 取出字符 判断回文 9 | var isPalindrome = function (head) { 10 | let arr = [] 11 | // 取出字符 12 | let curr = head 13 | while (curr) { 14 | arr.push(curr.val) 15 | curr = curr.next 16 | } 17 | // 双指针判断回文 18 | for (let i = 0, j = arr.length - 1; i < j; i++, j--) { 19 | if (arr[i] !== arr[j]) { 20 | return false 21 | } 22 | } 23 | return true 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/partition-equal-subset-sum/partition-equal-subset-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {boolean} 4 | */ 5 | // 解析:https://leetcode-cn.com/problems/last-stone-weight-ii/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-5lfv/ 6 | // 01背包 存在性问题: 外循环nums 内循环target target倒序且target>= nums[i] 7 | // 转移方程:dp[i] = dp[i] || dp[i-num] 8 | let canPartition = function (nums) { 9 | let total = 0 10 | for (const num of nums.values()) { 11 | total += num 12 | } 13 | if (total % 2 === 1) return false // 和是奇数 则无法分割成两堆 一样大的数组 14 | 15 | let target = Math.floor(total / 2) 16 | let dp = new Array(target + 1).fill(0) 17 | // 用于累加 更改后续的装态 否则都是false 无法更改状态 18 | dp[0] = true // 0不需要分割 即可以实现的 19 | // 循环nums 取出所有num 20 | for (const num of nums.values()) { 21 | // 循环target 递减,大于num。 因为要判断i-num的index是否可以分割。 22 | // 如果i-num可以分割,那么dp[i] 拿到这个num也能分割 23 | for (let i = target; i >= num; i--) { 24 | // 是否已经有其他数目可以分割 || 减去这个数目 上一个状态是否可以分割 如果可以分割 当前也可以分割 25 | dp[i] = dp[i] || dp[i - num] 26 | } 27 | } 28 | return dp[target] 29 | } 30 | -------------------------------------------------------------------------------- /src/leetCode/pascals-triangle/pascals-triangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} numRows 3 | * @return {number[][]} 4 | */ 5 | let generate = function (numRows) { 6 | // 头两层是固定的值 7 | if (numRows <= 0) return [] 8 | if (numRows === 1) return [[1]] 9 | if (numRows === 2) return [[1], [1, 1]] 10 | let arr = [[1], [1, 1]] 11 | // 根据上一层获取下一层 12 | function getRows(preRow) { 13 | let nextRow = [] 14 | // 上一层从第二个元素开始 前后相加 直至最后 15 | for (let i = 1; i < preRow.length; i++) { 16 | nextRow.push(preRow[i - 1] + preRow[i]) 17 | } 18 | // 规律头尾各有一个1 19 | nextRow.unshift(1) 20 | nextRow.push(1) 21 | return nextRow 22 | } 23 | // 从第三层开始计算 循环获取下一层 24 | for (let j = 2; j < numRows; j++) { 25 | const preRow = arr[j - 1] // 获取上一层 26 | const nextRow = getRows(preRow) // 通过上一层获取下一层 27 | arr.push(nextRow) // 添加当前层 28 | } 29 | return arr 30 | } 31 | -------------------------------------------------------------------------------- /src/leetCode/path-sum-ii/path-sum-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @param {number} targetSum 12 | * @return {number[][]} 13 | */ 14 | var pathSum = function (root, targetSum) { 15 | if (!root) return [] 16 | let res = [] 17 | const help = (node, count, listStr) => { 18 | if (!node) return 19 | count -= node.val 20 | if (!listStr) { 21 | // 初始化 22 | listStr = `${node.val}` 23 | } else { 24 | // 使用字符串保存走过的节点 这样在递归的时候 左子树和右子树不会互相影响 25 | listStr = `${listStr}+${node.val}` 26 | } 27 | // 直到遇到叶子节点 并且count为0 28 | if (count === 0 && !node.left && !node.right) { 29 | let list = listStr.split('+') // 字符串转数组 30 | res.push(list) 31 | return 32 | } 33 | help(node.left, count, listStr) 34 | help(node.right, count, listStr) 35 | } 36 | help(root, targetSum) 37 | return res 38 | } 39 | -------------------------------------------------------------------------------- /src/leetCode/path-sum/path-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @param {number} sum 11 | * @return {boolean} 12 | */ 13 | var hasPathSum = function (root, sum) { 14 | if (root === null) return false 15 | if (root.left === null && root.right === null) return root.val === sum // 没有节点的子节点 如果数字相同 即说明整条链路数字的和等于sum 16 | let res = sum - root.val // 减去本节点的值 得到下一节点 17 | return hasPathSum(root.left, res) || hasPathSum(root.right, res) 18 | } 19 | -------------------------------------------------------------------------------- /src/leetCode/perfect-squares/perfect-squares.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | // 动态规划 6 | let numSquares = function (n) { 7 | let dp = new Array(n + 1).fill(0) // dp 8 | // 查找到n为止 9 | for (let i = 0; i <= n; i++) { 10 | dp[i] = i // 最坏情况 每次加1 11 | // 查找到没有完全平方数可以减为止 12 | for (let j = 1; i - j * j >= 0; j++) { 13 | let count = j * j // 当前值的完全平方数 14 | const dpCountMinIndex = i - count // 减去这个值 获取减去这个值的最少数量 15 | let countMin = dp[dpCountMinIndex] + 1 // 加上count这个完全平方数 比对最小数量 16 | const preMin = dp[i] // 之前计算的最小需要完全平方数的数量 17 | dp[i] = Math.min(preMin, countMin) 18 | } 19 | } 20 | return dp[n] 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/leetCode/permutations/permutations.js: -------------------------------------------------------------------------------- 1 | var permute = function (nums) { 2 | let res = [] 3 | dfs([]) 4 | function dfs(path) { 5 | if (path.length === nums.length) { 6 | const item = [...path] // 复制path 引用类型 指针相同 7 | res.push(item) // 一条路径完成 8 | return 9 | } 10 | // 遍历决策树 11 | for (let num of nums.values()) { 12 | if (path.includes(num)) continue // 已存在的元素不再添加 防止重复 13 | path.push(num) // 每个节点 都选择一遍它的路径 14 | dfs(path) // 穷尽它的路径 回溯 15 | path.pop() // 撤销选择的节点 回归原先的状态 回溯 16 | } 17 | } 18 | return res 19 | } 20 | -------------------------------------------------------------------------------- /src/leetCode/qing-wa-tiao-tai-jie-wen-ti-lcof/qing-wa-tiao-tai-jie-wen-ti-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | 6 | // 变量缓存 7 | let numWays = function (n) { 8 | let a = 1; let 9 | b = 1 // 初始值 10 | for (let i = 2; i <= n; i++) { 11 | let res = (a + b) % 1000000007; // 取模 12 | [a, b] = [res, a] 13 | } 14 | return a // 每次更新的是a 15 | } 16 | 17 | // 数组 动态规划 18 | // var numWays = function (n) { 19 | // let arr = [1, 1, 2, 3] // 初始化 20 | // for (let i = 3; i <= n; i++) { 21 | // let res = arr[i - 1] + arr[i - 2] 22 | // arr[i] = res % 1000000007 // 取模 23 | // } 24 | // return arr[n] 25 | // }; 26 | -------------------------------------------------------------------------------- /src/leetCode/queue-reconstruction-by-height/queue-reconstruction-by-height.js: -------------------------------------------------------------------------------- 1 | /** 2 | 1. 按照身高排序 确定身高维度 前面的节点一定都比本节点高 3 | 2. 重点:下面直接按照k的下标插入 就能找到合适的位置 4 | 后面插入节点也不会影响前面已经插入的节点 因为它是低身高 不进行计算 5 | 比如: [ [ 7, 0 ], [ 7, 1 ], [ 6, 1 ], [ 5, 0 ], [ 5, 2 ], [ 4, 4 ] ] => [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 6 | * @param {number[][]} people 7 | * @return {number[][]} 8 | */ 9 | var reconstructQueue = function (people) { 10 | // 身高排序辅助函数 k比较小的排前面 11 | const help = (temp, item) => { 12 | if (temp[0] === item[0]) { 13 | // 身高相同 按照k排序 k比较小的 放在前面 14 | if (temp[1] < item[1]) { 15 | return true 16 | } 17 | } else if (temp[0] > item[0]) { 18 | // 身高比较高的 排在前面 19 | return true 20 | } 21 | return false // 停止插入 22 | } 23 | // 按照身高排序 确定身高维度 前面的节点一定都比本节点高 24 | // 插入排序 25 | for (let i = 0; i < people.length; i++) { 26 | let temp = people[i] 27 | let j = i 28 | while (j > 0 && help(temp, people[j - 1])) { 29 | people[j] = people[j - 1] 30 | j-- 31 | } 32 | people[j] = temp 33 | } 34 | // 重点贪心: 下面直接按照k的下标插入 就能找到合适的位置 35 | // 后面插入节点也不会影响前面已经插入的节点 因为它是低身高 不进行计算 36 | // 局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性 37 | // 全局最优:最后都做完插入操作,整个队列满足题目队列属性 38 | // 比如: [ [ 7, 0 ], [ 7, 1 ], [ 6, 1 ], [ 5, 0 ], [ 5, 2 ], [ 4, 4 ] ] => [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 39 | const len = people.length 40 | for (let i = 0; i < len; i++) { 41 | const item = people[i] 42 | // 删除 插入 43 | people.splice(i, 1) 44 | people.splice(item[1], 0, item) 45 | } 46 | return people 47 | } 48 | -------------------------------------------------------------------------------- /src/leetCode/range-sum-query-immutable/range-sum-query-immutable.js: -------------------------------------------------------------------------------- 1 | var NumArray = function (nums) { 2 | let total = nums.length 3 | this.res = new Array(total + 1).fill(0) 4 | for (let i = 0; i < total; i++) { 5 | // 求每个元素从头的元素总和 并缓存它 6 | this.res[i + 1] = nums[i] + this.res[i] 7 | } 8 | } 9 | 10 | 11 | NumArray.prototype.sumRange = function (left, right) { 12 | // 直接输出 结尾元素的和减去开始元素到起始元素的和即可 13 | return this.res[right + 1] - this.res[left] 14 | } 15 | 16 | // /** 17 | // * 普通方法 18 | // * @param {number[]} nums 19 | // */ 20 | // var NumArray = function (nums) { 21 | // this.nums = nums 22 | // }; 23 | 24 | // /** 25 | // * 每次循环都要重新计算 太麻烦了 26 | // * @param {number} left 27 | // * @param {number} right 28 | // * @return {number} 29 | // */ 30 | // NumArray.prototype.sumRange = function (left, right) { 31 | // let res = 0; 32 | // for (let i = left; i <= right; i++) { 33 | // res = res + this.nums[i] 34 | // } 35 | // return res 36 | // }; 37 | 38 | /** 39 | * Your NumArray object will be instantiated and called as such: 40 | * var obj = new NumArray(nums) 41 | * var param_1 = obj.sumRange(left,right) 42 | */ 43 | -------------------------------------------------------------------------------- /src/leetCode/relative-sort-array/relative-sort-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arr1 3 | * @param {number[]} arr2 4 | * @return {number[]} 5 | */ 6 | var relativeSortArray = function (arr1, arr2) { 7 | let m = new Map() 8 | // 设置map 9 | for (let i = 0; i < arr2.length; i++) { 10 | m.set(arr2[i], i + 1) // 值为index 升序 11 | } 12 | arr1.sort((a, b) => { 13 | let aM = m.get(a) 14 | let bM = m.get(b) 15 | if (aM && bM) { 16 | // 两个都在arr2 根据index升序 17 | return aM - bM 18 | } if (aM) { 19 | return -1 // a在map中 a排在b前面 20 | } if (bM) { 21 | return 1 // b排在a前面 22 | } 23 | // 两个都没有的情况 升序 24 | return a - b 25 | }) 26 | return arr1 27 | } 28 | -------------------------------------------------------------------------------- /src/leetCode/remove-all-adjacent-duplicates-in-string/remove-all-adjacent-duplicates-in-string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} S 3 | * @return {string} 4 | */ 5 | var removeDuplicates = function (S) { 6 | let stack = [] // 栈 7 | for (let i = 0; i < S.length; i++) { 8 | if (stack.length && stack[stack.length - 1] === S[i]) { 9 | // 如果当前要添加的值与栈中最后一个值相同 则删除栈中的值并且不添加当前值 10 | stack.pop() 11 | } else { 12 | stack.push(S[i]) 13 | } 14 | } 15 | return stack.join('') 16 | } 17 | -------------------------------------------------------------------------------- /src/leetCode/remove-duplicates-from-sorted-array/remove-duplicates-from-sorted-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | // 快慢指针 6 | var removeDuplicates = function (nums) { 7 | let j = 0 // 改变前面元素的指针 8 | // 从第二个开始与第一个匹配 9 | for (let i = 1; i < nums.length; i++) { 10 | if (nums[i] !== nums[j]) { 11 | // 值不同 更改下一个的值 答案只要求前面几个值变更 12 | j++ 13 | nums[j] = nums[i] // 更改前面几个元素 14 | } 15 | // 相同则跳过继续迭代 16 | } 17 | // 可以在这里删除后面的nums 18 | return j + 1 // 返回数量 19 | } 20 | 21 | // // 快慢指针 while写法 22 | // var removeDuplicates = function (nums) { 23 | // const n = nums.length 24 | // if (n === 0) { 25 | // return 0 26 | // } 27 | // let fast = 1; let 28 | // slow = 1 29 | // while (fast < n) { 30 | // if (nums[fast] !== nums[fast - 1]) { 31 | // nums[slow] = nums[fast] 32 | // ++slow 33 | // } 34 | // ++fast 35 | // } 36 | // return slow 37 | // } 38 | -------------------------------------------------------------------------------- /src/leetCode/remove-duplicates-from-sorted-list/remove-duplicates-from-sorted-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | 9 | // 循环 10 | var deleteDuplicates = function (head) { 11 | if (!head) return head 12 | let curr = head 13 | while (curr.next) { 14 | if (curr.val === curr.next.val) { 15 | curr.next = curr.next.next 16 | } else { 17 | curr = curr.next 18 | } 19 | } 20 | return head 21 | } 22 | 23 | // 快慢指针 快指针先走一步 根据值来对比 24 | // var deleteDuplicates = function (head) { 25 | // if (!head) return head 26 | // let fast = head.next 27 | // let slow = head 28 | // while (fast) { 29 | // if (fast.val === slow.val) { 30 | // // 删除fast 更新slow连接 更新fast 31 | // slow.next = fast.next 32 | // fast = slow.next 33 | // } else { 34 | // // 循环 35 | // fast = fast.next 36 | // slow = slow.next 37 | // } 38 | // } 39 | // return head 40 | // } 41 | 42 | // 哈希 43 | // var deleteDuplicates = function (head) { 44 | // let curr = head 45 | // let m = new Map() 46 | // while (curr) { 47 | // let pre = m.get(curr.val) 48 | // if (pre) { 49 | // // 删除 50 | // pre.next = curr.next // 上一个值 直接连接下一个值 51 | // curr = pre.next // 更新curr 52 | // } else { 53 | // m.set(curr.val, curr) 54 | // curr = curr.next 55 | // } 56 | // } 57 | // return head 58 | // }; 59 | 60 | -------------------------------------------------------------------------------- /src/leetCode/remove-k-digits/remove-k-digits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} num 3 | * @param {number} k 4 | * @return {string} 5 | */ 6 | var removeKdigits = function (num, k) { 7 | let stack = [] // 栈 8 | for (let i = 0; i < num.length; i++) { 9 | const ch = num.charAt(i) // 下一个要比较的值 10 | // while循环 如果下一个值比前一个值小的话 就一直往前删 直到没k 11 | // 如果下一个值比最后加入的值小的话就把上一个值删掉 12 | while (k > 0 && stack.length !== 0 && stack[stack.length - 1] > ch) { 13 | k-- 14 | stack.pop() 15 | } 16 | // 不能以0开头 17 | if (ch === '0' && stack.length === 0) continue 18 | stack.push(ch) 19 | } 20 | // k在上面还没减完 比如"150001" 3 会减去1、5 还剩以为 stack = [1] 21 | // 保留stack中的n-k位 22 | const res = stack.splice(0, stack.length - k).join('') 23 | return res.length === 0 ? '0' : res 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/remove-linked-list-elements/remove-linked-list-elements.js: -------------------------------------------------------------------------------- 1 | function ListNode(val, next) { 2 | this.val = (val === undefined ? 0 : val) 3 | this.next = (next === undefined ? null : next) 4 | } 5 | 6 | 7 | // 双指针 8 | const removeElements = function (head, val) { 9 | // 哨兵节点 假的头节点 10 | let sentinel = new ListNode(0) 11 | sentinel.next = head 12 | let pre = sentinel // 前一个节点 13 | let curr = sentinel.next // 后一个节点 14 | while (curr) { 15 | // 头节点相等的情况 16 | if (curr.val === val) { 17 | // 删除本节点 18 | pre.next = curr.next 19 | } else { 20 | // 本节点不相等 则记录这个安全节点 用于下次链接 21 | pre = curr 22 | } 23 | curr = curr.next 24 | } 25 | return sentinel.next 26 | } 27 | -------------------------------------------------------------------------------- /src/leetCode/remove-nth-node-from-end-of-list/remove-nth-node-from-end-of-list.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val) { 5 | * this.val = val; 6 | * this.next = null; 7 | * } 8 | */ 9 | /** 10 | * @param {ListNode} head 11 | * @param {number} n 12 | * @return {ListNode} 13 | */ 14 | // 快慢指针 15 | let removeNthFromEnd = function (head, n) { 16 | const preHead = new ListNode(0) 17 | preHead.next = head // 头节点 18 | let fast = preHead 19 | let slow = preHead 20 | // 快指针先走N步 21 | while (n--) { 22 | fast = fast.next 23 | } 24 | // 快慢一起前进 快指针先停下到结尾了 慢指针的下一个值就是要删除的节点 25 | while (fast && fast.next) { 26 | fast = fast.next 27 | slow = slow.next 28 | } 29 | // 删除第n个节点 30 | slow.next = slow.next.next 31 | return preHead.next 32 | } 33 | 34 | // /** 35 | // * Definition for singly-linked list. 36 | // * function ListNode(val) { 37 | // * this.val = val; 38 | // * this.next = null; 39 | // * } 40 | // */ 41 | // /** 42 | // * @param {ListNode} head 43 | // * @param {number} n 44 | // * @return {ListNode} 45 | // */ 46 | // // 数组形式 47 | // let removeNthFromEnd = function (head, n) { 48 | // const arr = [] 49 | // // 获取所有节点 50 | // while (head) { 51 | // arr.push(head.val) 52 | // head = head.next 53 | // } 54 | // // 从数组删除节点 55 | // const num = arr.length - n 56 | // arr.splice(num, 1) 57 | // let preHead = new ListNode(0) 58 | // let res = preHead 59 | // // 重新拼接节点 60 | // arr.forEach((item) => { 61 | // res.next = new ListNode(item) 62 | // res = res.next 63 | // }) 64 | // return preHead.next 65 | // } 66 | -------------------------------------------------------------------------------- /src/leetCode/reorder-list/reorder-list.js: -------------------------------------------------------------------------------- 1 | 2 | function ListNode(val, next) { 3 | this.val = (val === undefined ? 0 : val) 4 | this.next = (next === undefined ? null : next) 5 | } 6 | 7 | /** 8 | * 1. 添加所有节点 9 | * 2. 双指针添加节点 10 | * @param {ListNode} head 11 | * @return {void} Do not return anything, modify head in-place instead. 12 | */ 13 | const reorderList = function (head) { 14 | if (!head) return 15 | let stack = [] 16 | // 添加所有节点 17 | while (head) { 18 | stack.push(head) 19 | head = head.next 20 | } 21 | // 双指针添加节点 22 | let l = 0 23 | let end = stack.length - 1 24 | let newL = new ListNode() 25 | let cur = newL 26 | while (l <= end) { 27 | cur.next = stack[l] 28 | cur = cur.next 29 | if (l === end) break // 单数情况 会添加两次 取消一次 30 | cur.next = stack[end] 31 | cur = cur.next 32 | // 移动指针 33 | l++ 34 | end-- 35 | } 36 | cur.next = null // 更新最后一个节点的指向 37 | return newL.next 38 | } 39 | 40 | // 1. 找到最后一个节点 删除最后一个节点 防止前面的节点指向错误 41 | // 2. 头节点指向最后一个节点 42 | // 3. 递归改变所有头节点指向最后一个节点 43 | // 4. 返回头节点 链接起来 44 | // const reorderList = function (head) { 45 | // if (head === null || head.next === null) return 46 | // let pre = head 47 | // let last = head.next // 深一层 48 | // while (last && last.next !== null) { 49 | // last = last.next // 找到最后一个节点 50 | // pre = pre.next // 找到最后节点的前一个节点 51 | // } 52 | // pre.next = null // 删除最后一个节点 53 | // let temp = head.next 54 | // reorderList(temp) // 对除了第一个节点和最后一个节点进行递归 55 | // head.next = last // 指向最后一个节点 56 | // head.next.next = temp // 指向递归后的链表引用 57 | // return head 58 | // } 59 | -------------------------------------------------------------------------------- /src/leetCode/restore-ip-addresses/restore-ip-addresses.js: -------------------------------------------------------------------------------- 1 | // 回溯 2 | var restoreIpAddresses = function (s) { 3 | if (s.length > 12) return [] 4 | let result = [] 5 | fn(s, [], result) 6 | return result 7 | } 8 | // 递归 9 | function fn(remain, temp, result) { 10 | // 第四段 11 | if (temp.length === 3) { 12 | if (regular(remain)) { 13 | // 合法即为正确的值 14 | result.push([...temp, remain].join('.')) 15 | } 16 | return 17 | } 18 | // 每段长度都可能为1/2/3 19 | for (let i = 1; i < 4; i++) { 20 | // 合法才可继续 21 | if (regular(remain.substr(0, i))) { 22 | const strArr = [...temp, remain.substr(0, i)] // 字符段 23 | const str = remain.substr(i) // 剩下的字符串 24 | fn(str, strArr, result) 25 | } 26 | } 27 | } 28 | // 验证合法性 29 | function regular(s) { 30 | if (!s.length) return false 31 | return +s >= 0 && +s <= 255 && (s.length > 1 ? !!+s[0] : true) 32 | } 33 | -------------------------------------------------------------------------------- /src/leetCode/same-tree/same-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} p 11 | * @param {TreeNode} q 12 | * @return {boolean} 13 | */ 14 | var isSameTree = function (p, q) { 15 | // 递归退出 16 | if (p === null && q === null) { 17 | return true 18 | } if (p === null || q === null) { 19 | // 其中一个空了 20 | return false 21 | } 22 | // 节点值相同 23 | if (p.val !== q.val) { 24 | return false 25 | } 26 | // 递归子节点 27 | return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) 28 | } 29 | -------------------------------------------------------------------------------- /src/leetCode/shan-chu-lian-biao-de-jie-dian-lcof/shan-chu-lian-biao-de-jie-dian-lcof.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val) { 5 | * this.val = val; 6 | * this.next = null; 7 | * } 8 | */ 9 | 10 | // 哨兵虚拟节点 11 | var deleteNode = function (head, val) { 12 | let newHead = new ListNode(null) // 虚拟节点 13 | newHead.next = head 14 | let curr = newHead 15 | let before = newHead // 获取前一个节点 16 | while (curr) { 17 | if (curr.val === val) { 18 | // 删除 19 | before.next = curr.next 20 | curr = null 21 | break 22 | } 23 | before = curr 24 | curr = curr.next 25 | } 26 | return newHead.next 27 | } 28 | -------------------------------------------------------------------------------- /src/leetCode/shu-de-zi-jie-gou-lcof/shu-de-zi-jie-gou-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} A 10 | * @param {TreeNode} B 11 | * @return {boolean} 12 | */ 13 | var isSubStructure = function (A, B) { 14 | // 判断节点以及子节点 15 | const helper = (nodeA, nodeB) => { 16 | if (!nodeB) return true // B的全都匹配到了 返回true 17 | if (!nodeA) return false // A的递归完了 返回false 18 | if (nodeA.val !== nodeB.val) return false // 不相等 返回false 19 | return helper(nodeA.left, nodeB.left) && helper(nodeA.right, nodeB.right) // 相等 递归子树 子树必须完全相等 20 | } 21 | // A和B都不为空 才匹配 22 | // 匹配当前节点与B || 递归匹配左子树与B || 递归匹配右子树与B 23 | return (A !== null && B !== null) && (helper(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B)) 24 | } 25 | -------------------------------------------------------------------------------- /src/leetCode/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/shu-zu-zhong-zhong-fu-de-shu-zi-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var findRepeatNumber = function (nums) { 6 | // nums 7 | let map = {} 8 | for (let item of nums.values()) { 9 | if (map[item]) return item 10 | map[item] = '占位' 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/leetCode/simplify-path/simplify-path.js: -------------------------------------------------------------------------------- 1 | function simplifyPath(path) { 2 | let stack = path.split('/') // 根据/切割 转成栈 3 | let res = [] // 目录结果集 4 | // 从目录开始 循环栈 5 | for (let i = 0; i < stack.length; i++) { 6 | let item = stack[i] 7 | if (item === '' || item === '.') { 8 | // .以及多个/不操作 9 | continue 10 | } else if (item === '..') { 11 | if (res.length !== 0) { 12 | res.pop() // 前一个目录出栈 13 | } 14 | } else { 15 | // 添加目录到后面 16 | res.push(item) 17 | } 18 | } 19 | // 拼接/符号 20 | const result = `/${res.join('/')}` 21 | return result 22 | } 23 | -------------------------------------------------------------------------------- /src/leetCode/sort-list/sort-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @return {ListNode} 11 | */ 12 | // 选择排序 原地比较 13 | var sortList = function (head) { 14 | let now = head 15 | while (now) { 16 | let temp = now // 当前要跟前面比较的值 17 | let before = head // 指针指向开头 18 | // 当前值与前面的值比较 19 | while (before) { 20 | // 头部的链表循环 全等于当前比较链表 说明前面已经都比较过了 停止循环 21 | if (temp === before) { 22 | before = null 23 | continue 24 | } 25 | // 当前值最大值 比之前的值小 互相替换 直到temp替换成最大值 26 | if (before.val >= temp.val) { 27 | let count = before.val 28 | before.val = temp.val 29 | temp.val = count 30 | } 31 | // 循环比较之前值 32 | before = before.next 33 | } 34 | now = now.next // 比较下一个值 35 | } 36 | return head 37 | } 38 | -------------------------------------------------------------------------------- /src/leetCode/spiral-matrix/spiral-matrix.js: -------------------------------------------------------------------------------- 1 | // 环形遍历到底 中途退出 2 | var spiralOrder = function (matrix) { 3 | if (matrix.length === 0) return [] 4 | const res = [] 5 | let top = 0 6 | let bottom = matrix.length - 1 7 | let left = 0 8 | let right = matrix[0].length - 1 9 | // 即使top === bottom 或者 left === right 可能还剩一行或者一列 10 | while (top <= bottom && left <= right) { 11 | // 遍历上面和左边 12 | for (let i = left; i <= right; i++) res.push(matrix[top][i]) 13 | top++ // i = top 如果是最后一项 那么下面一个for循环不会运行 14 | for (let i = top; i <= bottom; i++) res.push(matrix[i][right]) 15 | right-- 16 | // 当top > bottom 或者 left > right 其中有条边界将交错 17 | if (res.length === matrix.length) break 18 | // 遍历右边和下面 19 | for (let i = right; i >= left; i--) res.push(matrix[bottom][i]) 20 | bottom-- 21 | for (let i = bottom; i >= top; i--) res.push(matrix[i][left]) 22 | left++ 23 | } 24 | return res 25 | } 26 | 27 | 28 | // /** 29 | // * 四层同时遍历 收缩 30 | // * 剩下一行 或者一列 单独遍历 31 | // * @param {number[][]} matrix 32 | // * @return {number[]} 33 | // */ 34 | // var spiralOrder = function (matrix) { 35 | // if (matrix.length === 0) return [] 36 | // const res = [] 37 | // let top = 0 38 | // let bottom = matrix.length - 1 39 | // let left = 0 40 | // let right = matrix[0].length - 1 41 | // while (top < bottom && left < right) { 42 | // for (let i = left; i < right; i++) res.push(matrix[top][i]) // 上层 43 | // for (let i = top; i < bottom; i++) res.push(matrix[i][right]) // 右层 44 | // for (let i = right; i > left; i--) res.push(matrix[bottom][i]) // 下层 45 | // for (let i = bottom; i > top; i--) res.push(matrix[i][left]) // 左层 46 | // right-- 47 | // top++ 48 | // bottom-- 49 | // left++ // 四个边界同时收缩,进入内层 50 | // } 51 | // // 剩下一行,从左到右依次添加 52 | // if (top === bottom && left <= right) { for (let i = left; i <= right; i++) res.push(matrix[top][i]) } 53 | // // 剩下一列,从上到下依次添加 54 | // else if (left === right && top <= bottom) { for (let i = top; i <= bottom; i++) res.push(matrix[i][left]) } 55 | // return res 56 | // } 57 | -------------------------------------------------------------------------------- /src/leetCode/split-a-string-in-balanced-strings/split-a-string-in-balanced-strings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {number} 4 | */ 5 | var balancedStringSplit = function (s) { 6 | let total = 0 7 | let num = 0 // 字符的数量 8 | // 带不动 9 | for (let i = 0; i < s.length; i++) { 10 | if (s[i] === 'L') { 11 | // 为L++ 12 | num++ 13 | } else { 14 | // 为R-- 15 | num-- 16 | } 17 | // 贪心:如果L和R的数量相等 num会变为0 即平衡字符串加1 18 | if (num === 0) { 19 | total++ 20 | } 21 | } 22 | return total 23 | } 24 | -------------------------------------------------------------------------------- /src/leetCode/subtree-of-another-tree/subtree-of-another-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @param {TreeNode} subRoot 12 | * @return {boolean} 13 | */ 14 | 15 | // 递归 16 | var isSubtree = function (root, subRoot) { 17 | if (subRoot === null) return true // 子树为空 说明判断完了 18 | if (root === null) return false // 父树为空 false 19 | // 判断父树的左右树 是否和子树相同 判断本树是否和子树相同 20 | return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot) || isSameRoot(root, subRoot) 21 | // 是否为相同的树 22 | function isSameRoot(root1, root2) { 23 | if (root1 === null && root2 === null) return true // 两个都为空为正确 24 | if (root1 === null || root2 === null) return false // 一个为空 就false 25 | if (root1.val !== root2.val) return false // 不相等 false 26 | return isSameRoot(root1.left, root2.left) && isSameRoot(root1.right, root2.right) // 所有的子节点也要完全相等 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/leetCode/swap-nodes-in-pairs/swap-nodes-in-pairs.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val) { 5 | * this.val = val; 6 | * this.next = null; 7 | * } 8 | */ 9 | // 不操作节点值 10 | var swapPairs = function (head) { 11 | let temp = new ListNode(0) 12 | temp.next = head // 头部节点缓存 13 | let pre = temp 14 | while (pre.next && pre.next.next) { 15 | const nowNode = pre.next // 当前节点 16 | const nextNode = pre.next.next // 下一个节点 17 | pre.next = nextNode // 1. 下个节点拼接 下下一个节点 18 | nowNode.next = nextNode.next // 下个节点指针指向下下个节点的后面 19 | nextNode.next = nowNode // 2. 下下个节点 拼接下个节点 20 | pre = pre.next.next // 3. 指针移动到 下下个节点 21 | } 22 | return temp.next 23 | } 24 | 25 | // /** 26 | // * @param {ListNode} head 27 | // * @return {ListNode} 28 | // */ 29 | // // 一次循环 每次操作两个值 30 | // var swapPairs = function (head) { 31 | // let pre = head // 初始节点 32 | // // 当前值和下一个值都存在 才交换 33 | // while (pre && pre.next) { 34 | // const nowVal = pre.val 35 | // const nextVal = pre.next.val 36 | // pre.val = nextVal 37 | // pre.next.val = nowVal 38 | // // 存在下下个值 获取下下个值 再两两交换 39 | // if (pre.next.next) { 40 | // pre = pre.next.next 41 | // } else { 42 | // // 不存在下下个值 更新pre 退出循环 43 | // pre = pre.next 44 | // } 45 | // } 46 | // return head // 返回操作过后的节点 47 | // } 48 | -------------------------------------------------------------------------------- /src/leetCode/symmetric-tree/symmetric-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | // /** 10 | // * 递归 同时移动指针 任何一棵树的左节点 等于另一颗的右节点 11 | // * @param {TreeNode} root 12 | // * @return {boolean} 13 | // */ 14 | // var isSymmetric = function (root) { 15 | // const checkNode = (leftRoot, rightRoot) => { 16 | // if (leftRoot === null && rightRoot === null) return true // 没有节点为true 17 | // if (leftRoot === null || rightRoot === null) return false // 其中一个为空 18 | // if (leftRoot.val !== rightRoot.val) return false // 值不等 19 | // // 左节点的左树和右节点的右树 右节点的的左树和左节点的右树 20 | // return checkNode(leftRoot.left, rightRoot.right) && checkNode(rightRoot.left, leftRoot.right) 21 | // } 22 | // return checkNode(root, root) 23 | // } 24 | 25 | // 栈实现 26 | var isSymmetric = function (root) { 27 | if (root == null || (root.left == null && root.right == null)) { 28 | return true 29 | } 30 | const stack = [root.left, root.right] 31 | while (stack.length) { 32 | const left = stack.pop() 33 | const right = stack.pop() 34 | if (left == null && right == null) { 35 | continue 36 | } 37 | if (left == null || right == null) { 38 | return false 39 | } 40 | if (left.val !== right.val) { 41 | return false 42 | } 43 | // 将左节点的左孩子, 右节点的右孩子放入队列 44 | stack.push(left.left) 45 | stack.push(right.right) 46 | // 将左节点的右孩子,右节点的左孩子放入队列 47 | stack.push(left.right) 48 | stack.push(right.left) 49 | } 50 | return true 51 | } 52 | -------------------------------------------------------------------------------- /src/leetCode/target-sum/target-sum.js: -------------------------------------------------------------------------------- 1 | // dfs深度优先 2 | 3 | let findTargetSumWays = function (nums, target) { 4 | let res = 0 5 | function dfs(index, cur) { 6 | // 数组 + - 分别都操作过一遍了 7 | if (index === nums.length) { 8 | if (cur === target) { 9 | res++ 10 | } 11 | return 12 | } 13 | dfs(index + 1, cur - nums[index]) 14 | dfs(index + 1, cur + nums[index]) 15 | } 16 | dfs(0, 0) // 搜索 17 | return res 18 | } 19 | 20 | 21 | // /** 22 | // * @param {number[]} nums 23 | // * @param {number} target 24 | // * @return {number} 25 | // */ 26 | // // https://leetcode-cn.com/problems/last-stone-weight-ii/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-5lfv/ 27 | // // 01背包:外循环nums 内循环target 倒序 且target>= num 28 | // // 不考虑顺序的组合问题:dp[i] += dp[i - num] 29 | // let findTargetSumWays = function (nums, target) { 30 | // let total = 0 31 | // for (const num of nums.values()) { 32 | // total += num 33 | // } 34 | // // 如果S大于sum,不可能实现,返回0 35 | // // 如果x不是整数,也就是S + sum不是偶数,不可能实现,返回0 36 | // if (total < target || (total + target) % 2 !== 0) return 0 37 | // // 我们想要的 S = 正数和 - 负数和 = x - y 38 | // // 而已知x与y的和是数组总和:x + y = sum 39 | // // 可以求出 x = (S + sum) / 2 = target 40 | // target = (total + target) / 2 // 正数目标值 41 | // if (target < 0) return 0 // 正数目标值需要大于0 42 | // let dp = new Array(target + 1).fill(0) 43 | // // 初始化累加值 44 | // dp[0] = 1 // 填满容易为0的背包只有一种方法 45 | // for (const num of nums.values()) { 46 | // for (let i = target; i >= num; i--) { 47 | // // eslint-disable-next-line operator-assignment 48 | // dp[i] = dp[i] + dp[i - num] // 当前的最小数量 49 | // } 50 | // } 51 | // return dp[target] 52 | // } 53 | 54 | -------------------------------------------------------------------------------- /src/leetCode/the-masseuse-lcci/the-masseuse-lcci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态规划、贪心 求出每个选择的最优值 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | 7 | let massage = function (nums) { 8 | let arr = [0, nums[0]] // 初始化 9 | for (let i = 2; i <= nums.length; i++) { 10 | // 选或者不选本次预约 哪个更合理 11 | // f(n) = Math.max(f(n-2) + nums[n - 1], f(n-1)) 12 | // 前天是否选择过预约+今天的预约 与 昨天预约的长度比较 13 | // 得出今天的最长长度 每天都是最长长度也就是最优子结构 14 | // 最后得出的就是最大值 15 | arr[i] = Math.max(arr[i - 2] + nums[i - 1], arr[i - 1]) 16 | } 17 | return arr[nums.length] 18 | } 19 | -------------------------------------------------------------------------------- /src/leetCode/three-steps-problem-lcci/three-steps-problem-lcci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | 6 | // 动态规划 7 | // 状态转移方程:fn = f(n-1)+f(n-2)+f(n-3) 8 | let waysToStep = function (n) { 9 | let m = 1e9 + 7 10 | let resArr = [0, 1, 2, 4] // 缓存结果 11 | // 从小到大 12 | for (let i = 4; i <= n; i++) { 13 | resArr[i] = (resArr[i - 1] + resArr[i - 2] + resArr[i - 3]) % m 14 | } 15 | return resArr[n] 16 | } 17 | -------------------------------------------------------------------------------- /src/leetCode/transpose-matrix/transpose-matrix.js: -------------------------------------------------------------------------------- 1 | // 将一个长方形横放改为竖放 每列为一个数组 是大数组的一个元素 2 | var transpose = function (A) { 3 | let result = [] 4 | // 横向遍历 5 | for (let i = 0; i < A[0].length; i++) { 6 | let row = [] 7 | // 纵向遍历 8 | for (let j = 0; j < A.length; j++) { 9 | // 添加每列的第一个元素 10 | row.push(A[j][i]) 11 | } 12 | result.push(row) 13 | } 14 | return result 15 | } 16 | -------------------------------------------------------------------------------- /src/leetCode/two-sum/two-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number[]} 5 | */ 6 | let twoSum = function (nums, target) { 7 | // 1. 设置一个map映射表 8 | let map = {} 9 | let answer = [] 10 | // 2. 循环值 如果在表内找到对应的映射 11 | for (const [index, value] of nums.entries()) { 12 | // 查找是否已经有对应结果了 13 | const countIndex = map[String(value)] 14 | if (countIndex !== undefined) { 15 | answer.push(countIndex, index) // 添加索引 16 | break 17 | } 18 | const count = target - value // 对应结果 19 | map[count] = index // 当前索引 20 | } 21 | // 3. 即return 22 | return answer 23 | } 24 | -------------------------------------------------------------------------------- /src/leetCode/unique-binary-search-trees/unique-binary-search-trees.js: -------------------------------------------------------------------------------- 1 | const numTrees = (n) => { 2 | const dp = new Array(n + 1).fill(0) 3 | dp[0] = 1 // 空树 4 | dp[1] = 1 // 一种 5 | for (let i = 2; i <= n; i++) { 6 | for (let j = 0; j <= i - 1; j++) { 7 | dp[i] += dp[j] * dp[i - j - 1] // 规律 左子树出来的形态有 aa 种,右子树出来的形态有 bb 种,则整个树的形态有 a * ba∗b 种 8 | } 9 | } 10 | return dp[n] 11 | } 12 | -------------------------------------------------------------------------------- /src/leetCode/valid-anagram/valid-anagram.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @param {string} t 4 | * @return {boolean} 5 | */ 6 | // 排序+双指针 7 | var isAnagram = function (s, t) { 8 | // 字符串转数组 9 | let sArr = s.split('') 10 | let tArr = t.split('') 11 | if (sArr.length !== tArr.length) return false // 不同数量 12 | // 对字符串进行排序 13 | sArr.sort((a, b) => a.localeCompare(b)) 14 | tArr.sort((a, b) => a.localeCompare(b)) 15 | for (let i = 0; i < sArr.length; i++) { 16 | // 两个字符串数组 有一个元素位置不同 则为错误 17 | if (sArr[i] !== tArr[i]) { 18 | return false 19 | } 20 | } 21 | return true 22 | } 23 | 24 | // 哈希 计算字符数量 25 | // var isAnagram = function (s, t) { 26 | // let m = new Map() 27 | // // 计算s的字符数量 28 | // for (let key of s) { 29 | // if (m.has(key)) { 30 | // let total = m.get(key) 31 | // m.set(key, total + 1) 32 | // } else { 33 | // m.set(key, 1) 34 | // } 35 | // } 36 | // for (let key of t) { 37 | // // 清空已有字符数量 38 | // if (m.has(key)) { 39 | // let total = m.get(key) 40 | // m.set(key, total - 1) 41 | // if (total - 1 === 0) m.delete(key) 42 | // } else { 43 | // // 有其他值 添加 44 | // m.set(key, 1) 45 | // } 46 | // } 47 | // return m.size === 0 // map被清空说明两边字符相等 否则数量有差异 48 | // }; 49 | -------------------------------------------------------------------------------- /src/leetCode/valid-parentheses/valid-parentheses.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 栈 4 | * @param {string} s 5 | * @return {boolean} 6 | */ 7 | let isValid = function (s) { 8 | let total = s.length 9 | let stack = [] 10 | let mapHsh = { 11 | ')': '(', 12 | '}': '{', 13 | ']': '[', 14 | } 15 | for (let i = 0; i < total; i++) { 16 | let item = s[i] 17 | if (mapHsh[item]) { 18 | // 1. 栈内没左符号 目前有右符号 则false 19 | // 2. 最后一个左符号应该先关闭: 栈内最后一个左符号 不匹配当前右符号 说明顺序错误 // "([)]" 20 | if (stack.length === 0 || stack[stack.length - 1] !== mapHsh[item]) { 21 | return false 22 | } 23 | stack.pop() 24 | } else { 25 | // 添加左符号 26 | stack.push(item) 27 | } 28 | } 29 | // 栈内符号消除完毕 30 | return stack.length === 0 31 | } 32 | -------------------------------------------------------------------------------- /src/leetCode/validate-binary-search-tree/validate-binary-search-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {boolean} 12 | */ 13 | // var isValidBST = function (root) { 14 | 15 | // }; 16 | 17 | function isValidBST(root) { 18 | function help(node, min, max) { 19 | if (node === null) return true 20 | // 如果有最小值 不小于最小值 21 | if (node.val <= min) return false 22 | // 如果有最大值 不超过最大值 23 | if (node.val >= max) return false 24 | return help(node.left, min, node.val) 25 | && help(node.right, node.val, max) 26 | } 27 | return help(root, -Infinity, Infinity) 28 | } 29 | 30 | // const helper = (root, lower, upper) => { 31 | // if (root === null) { 32 | // return true; 33 | // } 34 | // if (root.val <= lower || root.val >= upper) { 35 | // return false; 36 | // } 37 | // return helper(root.left, lower, root.val) && helper(root.right, root.val, upper); 38 | // } 39 | // var isValidBST = function(root) { 40 | // return helper(root, -Infinity, Infinity); 41 | // }; 42 | -------------------------------------------------------------------------------- /src/leetCode/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/yong-liang-ge-zhan-shi-xian-dui-lie-lcof.js: -------------------------------------------------------------------------------- 1 | var CQueue = function () { 2 | this.inStack = [] 3 | this.outStack = [] 4 | } 5 | 6 | CQueue.prototype.appendTail = function (value) { 7 | this.inStack.push(value) 8 | } 9 | 10 | CQueue.prototype.deleteHead = function () { 11 | if (!this.outStack.length) { 12 | if (!this.inStack.length) { 13 | return -1 14 | } 15 | this.in2out() 16 | } 17 | return this.outStack.pop() 18 | } 19 | 20 | CQueue.prototype.in2out = function () { 21 | while (this.inStack.length) { 22 | this.outStack.push(this.inStack.pop()) 23 | } 24 | } 25 | 26 | 27 | 28 | // var CQueue = function () { 29 | // this.stack = [] 30 | // }; 31 | 32 | 33 | // /** 34 | // * @param {number} value 35 | // * @return {void} 36 | // */ 37 | // CQueue.prototype.appendTail = function (value) { 38 | // this.stack.push(value) 39 | // }; 40 | 41 | // /** 42 | // * @return {number} 43 | // */ 44 | // CQueue.prototype.deleteHead = function () { 45 | // if (this.stack.length) return this.stack.shift() 46 | // return -1 47 | // }; 48 | -------------------------------------------------------------------------------- /src/leetCode/zui-xiao-de-kge-shu-lcof/zui-xiao-de-kge-shu-lcof.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arr 3 | * @param {number} k 4 | * @return {number[]} 5 | */ 6 | var getLeastNumbers = function (arr, k) { 7 | arr.sort((a, b) => a - b) 8 | return arr.splice(0, k) 9 | } 10 | -------------------------------------------------------------------------------- /src/scene/arrToTree/index.js: -------------------------------------------------------------------------------- 1 | // 大同小异 此题留给诸位 2 | const arr = [{ 3 | id: 2, 4 | name: '部门B', 5 | parentId: 0, 6 | }, 7 | { 8 | id: 3, 9 | name: '部门C', 10 | parentId: 1, 11 | }, 12 | { 13 | id: 1, 14 | name: '部门A', 15 | parentId: 2, 16 | }, 17 | { 18 | id: 4, 19 | name: '部门D', 20 | parentId: 1, 21 | }, 22 | { 23 | id: 5, 24 | name: '部门E', 25 | parentId: 2, 26 | }, 27 | { 28 | id: 6, 29 | name: '部门F', 30 | parentId: 3, 31 | }, 32 | { 33 | id: 7, 34 | name: '部门G', 35 | parentId: 2, 36 | }, 37 | { 38 | id: 8, 39 | name: '部门H', 40 | parentId: 4, 41 | }, 42 | ] 43 | 44 | function toTree(arr) { 45 | 46 | } 47 | console.log('toTree', toTree(arr)) 48 | -------------------------------------------------------------------------------- /src/scene/arrToTree/数组转树, 以及函数扩展.md: -------------------------------------------------------------------------------- 1 | 数组转树, 写完后问如果要在树中新增节点或者删除节点, 函数应该怎么扩展 2 | 3 | 函数扩展 4 | 5 | 增加一个对象参数,参数默认值为空对象, 如下。 6 | 7 | { 8 | deleteNodeArr: [], 9 | addNodeArr: [] 10 | } 11 | 校验对象属性是否有值,是否为数组。 12 | 13 | 如果是删除节点,则在数组转树之前,将节点在原始数组中删除,再对原始节点进行转树操作。 14 | 15 | 如果是增加节点,则直接增加在原始数组后面,再进行转树操作。 -------------------------------------------------------------------------------- /src/scene/compose/compose.js: -------------------------------------------------------------------------------- 1 | // 题目需求 2 | // 实现compose函数, 类似于koa的中间件洋葱模型 3 | 4 | let middleware = [] 5 | middleware.push((context, next) => { 6 | console.log(1) 7 | next() 8 | console.log(1.1) 9 | }) 10 | middleware.push((context, next) => { 11 | console.log(2) 12 | next() 13 | console.log(2.1) 14 | }) 15 | middleware.push((context, next) => { 16 | console.log(3) 17 | next() 18 | console.log(3.1) 19 | }) 20 | 21 | let fn = compose(middleware) 22 | fn() 23 | 24 | /* 25 | 1 26 | 2 27 | 3 28 | 3.1 29 | 2.1 30 | 1.1 31 | */ 32 | 33 | function compose(middleware) { 34 | 35 | } 36 | 37 | 38 | 39 | 40 | // 答案慎看 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | // /** 65 | // * @description: 思想 66 | // * 本质上是递归 通过数组下标,进行迭代, 67 | // * 执行当前迭代的next函数,遇到下一次迭代的nextFn,执行下一次迭代。 68 | // * next函数可能是个promise需要使用Promise.resolve(p) 69 | // * 限制是数组下标超过则return,数组到了最后一个元素, 需要再执行一次next函数,因为compose也可能传递next函数 70 | // */ 71 | // function compose(middleware) { 72 | // return function fn(context, next) { 73 | // return dispatch(0) // 初始化调用 74 | // function dispatch(i) { 75 | // if (i > middleware.length - 1) return // 循环结束限制 76 | // let fn = middleware[i] // 获取函数 77 | // if (i === middleware.length) fn = next // 最后执行的next 78 | // if (!fn) return Promise.resolve() // 没有item 79 | // const nextFn = dispatch.bind(context, i + 1) // 绑定下一个next的fn 80 | // const p = fn(context, nextFn) // 执行当前迭代的next, 遇到下一次迭代的nextFn 执行nextFn 81 | // try { 82 | // return Promise.resolve(p) // fn可能是个promise,需要resolve来等待一下 83 | // } catch (err) { 84 | // return Promise.reject(err) 85 | // } 86 | // } 87 | // } 88 | // } 89 | -------------------------------------------------------------------------------- /src/scene/compose/实现compose函数,类似koa洋葱组件.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/scene/compose/实现compose函数,类似koa洋葱组件.md -------------------------------------------------------------------------------- /src/scene/dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-17 12:24:17 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-04 16:07:51 6 | * FilePath : /js-base/src/scene/dom.js 7 | * description : 如何遍历一个dom树 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 递归写法 13 | function traversal(node) { 14 | // 对node的处理 15 | if (node && node.nodeType === 1) { 16 | console.log(node.tagName) 17 | } 18 | // 递归先序遍历子节点 19 | let { childNodes } = node 20 | let item 21 | for (let i = 0; i < childNodes.length; i++) { 22 | item = childNodes[i] 23 | if (item.nodeType === 1) { 24 | traversal(item) 25 | } 26 | } 27 | } 28 | 29 | 30 | // 广度遍历 栈写法 31 | function traversal2(node) { 32 | const stack = [] 33 | stack.push(node) 34 | while (stack.length > 0) { 35 | const elem = stack.pop() 36 | if (elem && elem.nodeType === 1) { 37 | console.log(elem.tagName) 38 | const { children } = elem 39 | const len = children.length 40 | // 子节点入栈 添加到头部 先进先出 41 | for (let i = 0; i < len; i++) { 42 | stack.unshift(children[i]) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/scene/event-loop.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author : OBKoro1 3 | * @Date : 2021-09-10 11:14:30 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-09-11 16:39:02 6 | * FilePath : /js-base/src/scene/ecent-loop.js 7 | * @description : 实战event-loop任务优先级 8 | * 掌握这些任务的优先级:setTimeout、promise.nextTick、setImmediate、promise、 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | // 13 | 14 | 15 | // 答案在下面 16 | setImmediate(() => { 17 | console.log(1) 18 | }, 0) 19 | setTimeout(() => { 20 | console.log(2) 21 | }, 0) 22 | new Promise((resolve) => { 23 | console.log(3) 24 | resolve() 25 | console.log(4) 26 | }).then(() => { 27 | console.log(5) 28 | }) 29 | async function test() { 30 | const a = await 9 31 | console.log(a) 32 | const b = await new Promise((resolve) => { 33 | resolve(10) 34 | }) 35 | console.log(b) 36 | } 37 | test() 38 | console.log(6) 39 | process.nextTick(() => { 40 | console.log(7) 41 | }) 42 | console.log(8) 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | // 微任务:nextTick比then优先级高 宏任务:setTimeout优先级比setImmediate高 82 | // process.nextTick > promise.then > setTimeout > setImmediate 83 | // 注意await也是promise 但是需要一个个promise添加进去 所以同一个await里面的promise的顺序可能被其他的promise插队 84 | // 解析:https://www.jianshu.com/p/a39d3e878d06 85 | // 答案:3 4 6 8 7 5 2 1 86 | -------------------------------------------------------------------------------- /src/scene/event-loop2.js: -------------------------------------------------------------------------------- 1 | // 拼多多 pdd-20211109 第二题 2 | // 说出输出 并且解释为什么 3 | 4 | async function async1() { 5 | console.log('async1 start') 6 | await async2() 7 | console.log('async1 end') 8 | } 9 | async function async2() { 10 | console.log('async2') 11 | } 12 | console.log('script start') 13 | setTimeout(() => { 14 | console.log('setTimeout') 15 | }, 0) 16 | async1() 17 | Promise.resolve(1) 18 | .then((res) => { 19 | console.log('promise1', res) 20 | }) 21 | .then((res) => { 22 | console.log('promise2', res) 23 | return Promise.reject(1) 24 | }) 25 | .then((res) => { 26 | console.log('promise3', res) 27 | }) 28 | .catch((e) => { 29 | console.log('promise4', e) 30 | }) 31 | .then((res) => { 32 | console.log('promise5', res) 33 | }) 34 | console.log('script end') 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | // 答案慎看 49 | // 答案慎看 50 | // 答案慎看 51 | // 答案慎看 52 | // 答案慎看 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | // 第二个输出 正确答案: 62 | // script start 63 | // async1 start 没到await之前都算同步代码 64 | // async2 这也是同步代码 因为没有await 65 | // script end 66 | // async1 end 第一个异步结果 67 | // promise1 1 // promise链式第一个回调 68 | // promise2 undefined 第二个链式 没有返回结果 所以是undefined 69 | // promise4 1 链式回调catch reject的值 70 | // promise5 undefined 没有返回结果 71 | // setTimeout 宏任务回调 异步任务时间短的话 会不断插入到异步队列当中 宏任务需要等待异步任务结束 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/scene/find-target.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-11-09 17:56:27 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-09 18:02:54 6 | * description : 拼多多 找到数组中相加为target的两个值 7 | */ 8 | // 拼多多 pdd-20211109 第四题 9 | // 找到数组中相加为target的两个值 10 | // arr = [ 2, 14, -2 ,9,10,-5,7] target = 9 11 | // 输出对应值的下标: [0,6] 12 | 13 | // 先说一下思路 然后实现代码 14 | 15 | function findTarget(arr, target) { 16 | 17 | } 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | // 找到数组中相加为target的两个值 32 | // arr = [ 2, 14, -2 ,9,10,-5,7] target = 9 33 | // 输出对应值的下标: [0,6] 34 | 35 | // function findTarget(arr, target) { 36 | // let hash = {} 37 | // for (let i = 0; i < arr.length; i++) { 38 | // let item = arr[i] 39 | // // 找到目标值 40 | // if (hash[item]) { 41 | // return [hash[item], i] 42 | // } 43 | // // 没找到 44 | // let num = target - item 45 | // hash[num] = i 46 | // } 47 | // return false 48 | // } 49 | -------------------------------------------------------------------------------- /src/scene/findAll.js: -------------------------------------------------------------------------------- 1 | // 求出一个二维数组[[A, B], [a, b], [1, 2]]所有排列组合 2 | 3 | // 输入[[A, B], [a, b], [1, 2]] 4 | 5 | // 输出[Aa1, Aa2, Ab1, Ab2, Ba1, Ba2, Bb1, Bb2] 6 | 7 | // 循环老数组拼接新数组元素 8 | const getResult = (arrA, arrB) => { 9 | if (!Array.isArray(arrA) || !Array.isArray(arrB)) { 10 | return 11 | } 12 | if (arrA.length === 0) { 13 | return arrB 14 | } 15 | if (arrA.length === 0) { 16 | return arrA 17 | } 18 | const result = [] 19 | for (let i = 0; i < arrA.length; i++) { 20 | for (let j = 0; j < arrB.length; j++) { 21 | // 双重遍历拼接老数组成员与新数组成员 22 | result.push(String(arrA[i]) + String(arrB[j])) 23 | } 24 | } 25 | return result 26 | } 27 | 28 | // 递归 数组出栈 每个都与其他元素拼接 29 | const findAll = (arr) => { 30 | if (arr.length === 1) { 31 | return arr[0] 32 | } 33 | const temp = arr.shift() 34 | return getResult(temp, findAll(arr)) 35 | } 36 | 37 | // 测试 38 | const oldArr = [['A', 'B'], ['a', 'b'], ['1', '2']] 39 | 40 | console.log('findAll', findAll(oldArr)) 41 | -------------------------------------------------------------------------------- /src/scene/getAllTag.js: -------------------------------------------------------------------------------- 1 | // B站: 获取页面中的所有标签名 2 | 3 | // 自带的方法 4 | 5 | function getAllTagApi() { 6 | const arr = document.getElementsByTagName('*') // 获取所有标签 7 | const resArr = [] 8 | for (let i = 0; i < arr.length; i++) { 9 | const tagName = arr[i].localName // 获取标签名 10 | // 去重 11 | if (!resArr.includes(tagName)) { 12 | resArr.push(tagName) 13 | } 14 | } 15 | return resArr 16 | } 17 | 18 | console.log('getAllTagApi', getAllTagApi()) 19 | 20 | // 正则匹配的方式 21 | function getAllTag() { 22 | const resArr = [] 23 | const str = document.body.innerHTML 24 | const reg = /<(\w+)/g // 匹配标签名 加g全局匹配才可循环 25 | let match = null 26 | // 循环匹配 27 | // eslint-disable-next-line no-cond-assign 28 | while ((match = reg.exec(str))) { 29 | const tagName = match[1] 30 | // 去重 31 | if (!resArr.includes(tagName)) { 32 | resArr.push(tagName) 33 | } 34 | } 35 | return resArr 36 | } 37 | console.log('111', getAllTag()) 38 | 39 | // 递归遍历dom的形式 40 | -------------------------------------------------------------------------------- /src/scene/multiplication.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-10-11 18:39:34 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-04 16:04:22 6 | * FilePath : /js-base/src/scene/multiplication.js 7 | * description : 累乘和累乘缓存 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 累乘所有参数 13 | // multiplication(1, 2 , 3) = 1 * 2 * 3 = 6 14 | function multiplication() { 15 | } 16 | 17 | console.log('res', multiplication(1, 2, 3)) 18 | 19 | // 实现累乘缓存 20 | // 缓存输出 1,2, 3 下次 2, 3, 1 也能直接获取结果 21 | function multiplicationCatch() { 22 | } 23 | 24 | let multiplicationCatchInstance = multiplicationCatch() 25 | 26 | 27 | console.log('multiplicationCatch', multiplicationCatchInstance(1, 2, 3), multiplicationCatchInstance(2, 3, 1)) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | // 答案慎看 44 | // 答案慎看 45 | // 答案慎看 46 | // 答案慎看 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | // 实现累乘 62 | // function multiplication(...params) { 63 | // const res = params.reduce((cur, next) => { 64 | // return cur * next 65 | // }, 1) 66 | // return res 67 | // } 68 | 69 | 70 | // // 思路:排序, 字符串化, 存在对象里面 71 | // // 取值、计算 72 | 73 | // // 累乘 缓存 74 | // function multiplicationCatch() { 75 | // let map = {} 76 | // return function(...params) { 77 | // params.sort((a, b) => a - b) // 排序参数 78 | // let key = params.join(',') 79 | // // 是否有缓存 80 | // if (map[key]) { 81 | // return map[key] 82 | // } else { 83 | // // 没缓存过 84 | // const res = params.reduce((cur, next) => { 85 | // return cur * next 86 | // }, 1) 87 | // map[key] = res 88 | // return res 89 | // } 90 | // } 91 | // } 92 | // let multiplicationCatchInstance = multiplicationCatch() 93 | 94 | 95 | // console.log('multiplicationCatch', multiplicationCatchInstance(1, 2, 3), multiplicationCatchInstance(2, 3, 1)) 96 | -------------------------------------------------------------------------------- /src/scene/numAdd.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-17 21:06:19 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-09-17 21:06:21 6 | * FilePath : /js-base/src/scene/numAdd.js 7 | * description : 如何通过代码解决浮点数计算不准的问题以及浮点数不准的原因。 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 思路:小数转整数来操作 13 | function add(...args) { 14 | // 最小的小数有几位 15 | const maxLen = Math.max.apply( 16 | null, 17 | args.map((item) => { 18 | const str = String(item).split('.')[1] 19 | return str ? str.length : 0 20 | }), 21 | ) 22 | // 最小小数 转成整数 需要的倍数 23 | const baseMultiple = 10 ** maxLen 24 | return ( 25 | // 小数乘以倍数 再除以倍数 还原小数点 26 | args.reduce((sum, cur) => sum + cur * baseMultiple, 0) / baseMultiple 27 | ) 28 | } 29 | console.log(add(0.1, 0.2)) // => 0.3 30 | console.log(add(10, 11)) // => 21 31 | console.log(add(0.001, 0.003)) // => 0.004 32 | console.log(add(0.001, 0.003, 0.005)) // => 0.009 33 | console.log(add(0.001)) // => 0.001 34 | 35 | // 原因 36 | // 不仅 JavaScript,所有遵循 IEEE 754 规范的语言都是如此; 37 | // 在JavaScript中,所有的Number都是以64-bit的双精度浮点数存储的; 38 | // 双精度的浮点数在这64位上划分为3段,而这3段也就确定了一个浮点数的值,64bit的划分是“1-11-52”的模式,具体来说: 39 | // 1.就是1位最高位(最左边那一位)表示符号位,0表示正,1表示负; 40 | // 2.11位表示指数部分; 41 | // 3.52位表示尾数部分,也就是有效域部分 42 | -------------------------------------------------------------------------------- /src/scene/numberToCn/数字转汉语输出、汉语转数字输出.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/scene/numberToCn/数字转汉语输出、汉语转数字输出.md -------------------------------------------------------------------------------- /src/scene/promise/retry.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-async-promise-executor */ 2 | /* eslint-disable no-await-in-loop */ 3 | 4 | // 题目:实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject 5 | 6 | Promise.retry = function (promiseFn, times = 3) { 7 | 8 | } 9 | 10 | // 模拟promise 11 | function getProm() { 12 | const n = Math.random() 13 | return new Promise((resolve, reject) => { 14 | setTimeout(() => (n > 0.9 ? resolve(n) : reject(n)), 1000) 15 | }) 16 | } 17 | Promise.retry(getProm) 18 | 19 | // 答案慎看 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | // /** 67 | // * @description: 请求重试 68 | // * @param {type} promiseFn 69 | // * @param {type} times 70 | // * @param {type} reject 71 | // */ 72 | // Promise.retry = function (promiseFn, times = 3) { 73 | // return new Promise(async (resolve, reject) => { 74 | // // 重试次数 75 | // while (times--) { 76 | // try { 77 | // let ret = await promiseFn() 78 | // // 执行成功直接返回 79 | // resolve(ret) 80 | // break 81 | // } catch (error) { 82 | // // 重试机会 83 | // console.log(`还剩${times}`) 84 | // if (!times) { 85 | // // 重试结束,抛出错误 86 | // reject(error) 87 | // } 88 | // } 89 | // } 90 | // }) 91 | // } 92 | -------------------------------------------------------------------------------- /src/scene/proto-console.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-11-09 17:53:41 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-09 18:01:18 6 | * description : 拼多多 原型链输出 7 | */ 8 | 9 | 10 | 11 | // pdd-20211109 拼多多 原型链输出 12 | // 第一题 13 | // 说出输出 并且解释为什么 14 | 15 | function Foo() { 16 | Foo.prototype.a = function () { 17 | console.log(1) 18 | } 19 | this.a = function () { 20 | console.log(2) 21 | } 22 | } 23 | Foo.a = function () { 24 | console.log(3) 25 | } 26 | Foo.prototype.a = function () { 27 | console.log(4) 28 | } 29 | Foo.a() 30 | let obj = new Foo() 31 | obj.a() 32 | Foo.prototype.a() 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | // 答案慎看 47 | // 答案慎看 48 | // 答案慎看 49 | // 答案慎看 50 | // 答案慎看 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | // 第一个输出 正确答案 60 | 61 | // Foo.a() 3 // 直接找Foo这个函数对象上面的a 62 | // let obj = new Foo() 63 | // obj.a() 2 在this上找到a 不查找原型链 64 | // Foo.prototype.a() 1 // 在构造函数实例化的时候 被重写了 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/scene/reportInterval/reportInterval.js: -------------------------------------------------------------------------------- 1 | function createRepeat(callBack, repeat, interval) { 2 | 3 | } 4 | 5 | const fn = createRepeat(console.log, 3, 4) 6 | 7 | fn('helloWorld') // 每4秒输出一次helloWorld, 输出3次 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | // 答案慎看 16 | // 答案慎看 17 | // 答案慎看 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | // function createRepeat(callBack, repeat, interval) { 43 | // let repeatNum = 0 44 | // let intervalId = null 45 | // return (...param) => { 46 | // intervalId = setInterval(() => { 47 | // console.log(new Date()) 48 | // callBack(...param) 49 | // repeatNum += 1 50 | // if (repeatNum >= repeat) { 51 | // clearInterval(intervalId) 52 | // intervalId = null 53 | // } 54 | // }, interval * 1000) 55 | // } 56 | // } 57 | -------------------------------------------------------------------------------- /src/scene/reportInterval/每4秒输出一次, 输出3次.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/src/scene/reportInterval/每4秒输出一次, 输出3次.md -------------------------------------------------------------------------------- /src/scene/symbol-close.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-11-09 17:54:53 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-09 18:02:16 6 | * description : 判断符号组成的字符串是否正确 7 | */ 8 | 9 | 10 | 11 | // 第三题 12 | // 输入 '(' ')' '[' ']' '{' '}' 组成的字符串 判断是否正确 13 | // 输入 '()[{}]' true 14 | // '({[}])' false 15 | 16 | // 先说一下思路 然后实现代码 17 | 18 | function test(str) { 19 | 20 | } 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | // 答案慎看 35 | // 答案慎看 36 | // 答案慎看 37 | // 答案慎看 38 | // 答案慎看 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | // 输入 '(' ')' '[' ']' '{' '}' 组成的字符串 判断是否正确 63 | // 输入 '()[{}]' true 64 | // '({[}])' false 65 | 66 | // function test(str) { 67 | // let stack = [] // 栈 68 | // // 关闭符和开始符的映射 69 | // let hash = { 70 | // ')': '(', 71 | // ']': '[', 72 | // '}': '{', 73 | // } 74 | // for (let i = 0; i < str.length; i++) { 75 | // let item = str[i] 76 | // let isClose = hash[item] // 关闭符 对应的开始符 77 | // // 如果是关闭的话 78 | // if (isClose) { 79 | // let last = stack[stack.length - 1] // 最后一个 80 | // if (last === isClose) { 81 | // // 匹配到开始符 82 | // stack.pop() // 出栈 83 | // } else { 84 | // return false // 不匹配 85 | // } 86 | // } else { 87 | // // 不是关闭 入栈 88 | // stack.push(item) 89 | // } 90 | // } 91 | // if (stack.length === 0) return true 92 | // return false 93 | // } 94 | -------------------------------------------------------------------------------- /src/scene/time.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-13 16:14:51 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-04 16:02:13 6 | * FilePath : /js-base/src/scene/time.js 7 | * description : JS转换时间戳为刚刚、几分钟前、几小时前、几天前、几周前、几个月前等格式 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | // 思路:获取时间戳的差 13 | // 时间差除以时间格式需要的倍数 如果超过1 则表示时间差在这个等级中 14 | function getDateDiff(dateTimeStamp) { 15 | const now = Date.now() 16 | const diffValue = now - dateTimeStamp 17 | if (diffValue < 0) { 18 | console.error('结束日期不能小于开始日期!') 19 | return 20 | } 21 | // 时间格式转换需要的数字倍数 22 | const minute = 1000 * 60 // 23 | const hour = minute * 60// 24 | const day = hour * 24 // 几天前 25 | const month = day * 30 // 月 26 | // 时间差 除以时间格式需要的倍数 27 | const monthC = diffValue / month 28 | const weekC = diffValue / (7 * day) 29 | const dayC = diffValue / day 30 | const hourC = diffValue / hour 31 | const minC = diffValue / minute 32 | // 如果超过1 则表示时间差在这个等级中 33 | let result = '' 34 | if (monthC >= 1) { 35 | result = `发表于${parseInt(monthC, 10)}个月前` 36 | } else if (weekC >= 1) { 37 | result = `发表于${parseInt(weekC, 10)}周前` 38 | } else if (dayC >= 1) { 39 | result = `发表于${parseInt(dayC, 10)}天前` 40 | } else if (hourC >= 1) { 41 | result = `发表于${parseInt(hourC, 10)}个小时前` 42 | } else if (minC >= 1) { 43 | result = `发表于${parseInt(minC, 10)}分钟前` 44 | } else result = '刚刚发表' 45 | return result 46 | } 47 | 48 | // 测试代码 49 | console.log(getDateDiff(Date.now() - 10)) // 刚刚 50 | console.log(getDateDiff(Date.now() - 1000 * 60 * 5)) // 五分钟前 51 | console.log(getDateDiff(Date.now() - 1000 * 60 * 60 * 2)) // 2小时前 52 | console.log(getDateDiff(Date.now() - 1000 * 60 * 60 * 24 * 3)) // 3天前 53 | console.log(getDateDiff(Date.now() - 1000 * 60 * 60 * 24 * 30 * 4)) // 四个月前 54 | -------------------------------------------------------------------------------- /src/scene/toThousands.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2021-09-10 17:04:06 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2021-11-04 16:00:04 6 | * FilePath : /js-base/src/scene/toThousands.js 7 | * description : 数字千分位处理 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | const oldNum = 13333111122.22 13 | 14 | // 正则 15 | function toThousands1(num) { 16 | // 至少三位数 17 | num = parseFloat(num.toFixed(3)) 18 | // 小数点处理 19 | let [integer, decimal] = String.prototype.split.call(num, '.') 20 | // 仅当前面有一个数字或者两个数字 后面跟着三个数字 才匹配 +表示匹配3个数字多次 21 | // $&表示用于匹配的字符串 22 | integer = integer.replace(/\d{1,3}(?=(\d{3})+$)/g, '$&,') 23 | return `${integer}${decimal ? `.${decimal}` : ''}` 24 | } 25 | 26 | // 数字千分位处理 27 | function toThousands2(num) { 28 | // 小数点 29 | let [integer, decimal] = String.prototype.split.call(num, '.') 30 | const s = String(integer) // 数字一般都转字符操作 31 | const arr = [] 32 | let j = 0 // 匹配当前循环的数字 是不是3的倍数 33 | // 倒序拼接 34 | for (let i = s.length - 1; i >= 0; i--) { 35 | arr.push(s[i]) 36 | j++ 37 | // 如果是3的倍数 并且前面还有数字的话 加一个, 38 | if (j % 3 === 0 && i !== 0) { 39 | arr.push(',') 40 | } 41 | } 42 | // 颠倒数组 转字符串 43 | integer = arr.reverse().join('') 44 | // 小数点 45 | if (decimal) { 46 | integer = `${integer}.${decimal}` 47 | } 48 | 49 | return integer 50 | } 51 | 52 | console.log(toThousands1(oldNum)) 53 | console.log(toThousands2(oldNum)) 54 | -------------------------------------------------------------------------------- /src/scene/traffic-lights.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | /* 3 | * Author : OBKoro1 4 | * Date : 2021-10-13 14:45:44 5 | * LastEditors : OBKoro1 6 | * LastEditTime : 2021-11-04 15:58:01 7 | * FilePath : /js-base/src/scene/traffic-lights.js 8 | * description : 红绿灯算法 9 | * koroFileheader VSCode插件 10 | * Copyright (c) 2021 by OBKoro1, All Rights Reserved. 11 | */ 12 | 13 | // 有黄绿红,他们各自亮灯的持续时间是 1s,2s,3s 如此反复。 14 | // 还有暂停开始之类的功能:https://juejin.cn/post/6844903784187953159 15 | 16 | 17 | function timePromise(time) { 18 | return new Promise((resolve, reject) => { 19 | setTimeout(resolve, time) 20 | }) 21 | } 22 | async function setColor(i, color) { 23 | console.log('设置颜色', color) 24 | await timePromise(i) 25 | } 26 | 27 | async function run() { 28 | // 这里用了while循环 递归也可以 29 | while (true) { 30 | await setColor(3000, 'red') 31 | await setColor(2000, 'green') 32 | await setColor(1000, 'yellow') 33 | } 34 | } 35 | 36 | run() 37 | -------------------------------------------------------------------------------- /src/scene/url-parse.js: -------------------------------------------------------------------------------- 1 | // 小红书一面 2 | // 编码: 3 | // 解析url为: 4 | // { 5 | // "protocol": "http", 6 | // "hoshostname": "www.domain.com", 7 | // "path": "order", 8 | // "query": { 9 | // "user": "anonymous", 10 | // "id": "456", 11 | // "city": "北京", 12 | // "enabled": true 13 | // } 14 | // } 15 | 16 | // 需要解析的链接: 17 | const url = 'http://www.domain.com/order?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled' 18 | parse(url) 19 | 20 | function parse(url) { 21 | const protocolArr = url.split('://') 22 | const protocol = protocolArr[0] 23 | const hostnameArr = protocolArr[1].split('/') 24 | const hoshostname = hostnameArr[0] 25 | const pathArr = hostnameArr[1].split('?') 26 | const path = pathArr[0] 27 | const queryArr = pathArr[1].split('&') 28 | const query = {} 29 | queryArr.forEach((item) => { 30 | // 未指定值得 key 约定为 true 31 | if (item.indexOf('=') === -1) { 32 | query[item] = true 33 | return 34 | } 35 | const itemArr = item.split('=') 36 | const key = itemArr[0] 37 | // 解析中文参数 38 | const value = decodeURI(itemArr[1]) 39 | // 重复出现的 key 要组装成数组 40 | if (query[key] !== undefined) { 41 | if (!Array.isArray(query[key])) { 42 | query[key] = [query[key]] 43 | } 44 | query[key].push(value) 45 | } else { 46 | query[key] = value 47 | } 48 | }) 49 | return { 50 | protocol, 51 | hoshostname, 52 | path, 53 | query, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/scene/vDomToDom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/sceneDesign/前端接口防刷.md: -------------------------------------------------------------------------------- 1 | 2 | 前端防刷: 3 | 4 | * 接口防抖,loadding。 5 | * 前端使用验证码。 6 | * 前后端,请求参数采取签名加密, 比如带个时间戳,过期则是失效。 7 | * 前端:源ip请求个数限制。对请求来源的ip请求个数做限制。 8 | 9 | 后端防刷: 10 | * 请求头校验: 11 | * 源ip请求个数限制。对请求来源的ip请求个数做限制。 12 | * User-Agent校验, User-Agent 首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。 比如:Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 13 | * Referer限频 14 | * Referer限制 15 | * 将对方的IP地址记录在LocalStorage里面,考虑相同公司,小区可能使用同一个IP, 适当限制相同IP重复抽奖。 16 | * 前后端,请求参数采取签名加密。 17 | -------------------------------------------------------------------------------- /src/sceneDesign/大数据列表.md: -------------------------------------------------------------------------------- 1 | ### 【场景设计】 2 | 3 | 大数据列表如何设计平滑滚动和加载,下滑再上滑的操作,上下两个buffer区间如何变化和加载数据。 4 | 5 | ### 虚拟列表实现原理: 6 | 7 | 通过监听onScroll,监听表格滚动条偏移量,节选适当数量的部分数据,进行可视区内容渲染。 8 | 9 | 通过列表的固定高度区域,并设置每列的固定高度,计算当时滚动条所停留的区域对应的哪一条数据,并预留足够的buffer,渲染数据。 10 | 11 | buffer区域留的数据量,上面留百分之五十的数据下面留百分之五十的数据。 12 | 13 | ### react-window接受的数据 14 | 15 | * 容器的高度,用于计算每个视图能展示多少项 16 | * 每项的高度 - 用于计算最终高度、滚动条停留在第几项 17 | * 项的数量 - 用于计算虚拟列表的最终高度,设置滚动条大小 18 | 19 | -------------------------------------------------------------------------------- /src/sceneDesign/转盘组件设计.md: -------------------------------------------------------------------------------- 1 | 设计一个转盘组件, 需要考虑什么, 需要和业务方协调好哪些技术细节? 2 | 3 | 1. 首先会先做一下调研,查一下有没有成熟的库,这样可以避免很多隐藏的坑。 4 | 2. 如果没有很适配的库,根据库的相关配置,了解做这类需求需要注意的点。 5 | 3. 根据需求文档,将这些点整理成文档,与业务方去探讨,看有没有遗漏的部分。 6 | 4. 比如常规的文字换行,中奖概率,中奖背景区域设置,抽奖转动速度,中奖停止时间,停止动画,中奖后的动效。 7 | 5. 确认好需求之后,和业务方沟通参数,列好参数文档。 8 | -------------------------------------------------------------------------------- /static/run-code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/static/run-code.jpg -------------------------------------------------------------------------------- /static/vscode-debugger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/static/vscode-debugger.jpg -------------------------------------------------------------------------------- /static/web-basic-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OBKoro1/web-basics/3abe30a57eca6494dfb020aca717afdaf58f5588/static/web-basic-example.gif -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : OBKoro1 3 | * Date : 2022-01-12 16:45:42 4 | * LastEditors : OBKoro1 5 | * LastEditTime : 2022-01-16 15:48:43 6 | * FilePath : /js-base/test.js 7 | * description : 8 | * koroFileheader VSCode插件 9 | * Copyright (c) 2022 by OBKoro1, All Rights Reserved. 10 | */ 11 | 12 | 13 | function foo() { 14 | 15 | } 16 | --------------------------------------------------------------------------------