diff --git a/.github/workflows/compress.yml b/.github/workflows/compress.yml
new file mode 100644
index 0000000..45e81e1
--- /dev/null
+++ b/.github/workflows/compress.yml
@@ -0,0 +1,41 @@
+name: Compress
+
+on:
+ push:
+ branches: [main]
+ paths:
+ - "**.jpg"
+ - "**.jpeg"
+ - "**.png"
+ - "**.webp"
+ workflow_dispatch:
+
+jobs:
+ compress:
+ runs-on: ubuntu-latest
+ if: github.repository == 'doocs/coding-interview'
+ steps:
+ - name: Checkout Branch
+ uses: actions/checkout@v2
+
+ - name: Compress Images
+ id: calibre
+ uses: calibreapp/image-actions@main
+ with:
+ githubToken: ${{ secrets.ACTION_TOKEN }}
+ compressOnly: true
+
+ - name: Commit Files
+ if: |
+ steps.calibre.outputs.markdown != ''
+ run: |
+ git config --local user.email "szuyanglb@outlook.com"
+ git config --local user.name "yanglbme"
+ git commit -m "chore: auto compress images" -a
+
+ - name: Push Changes
+ if: |
+ steps.calibre.outputs.markdown != ''
+ uses: ad-m/github-push-action@master
+ with:
+ github_token: ${{ secrets.ACTION_TOKEN }}
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..2178acc
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,64 @@
+name: Build and deploy
+
+on:
+ push:
+ branches: [main]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 9
+
+ - name: Get pnpm store directory
+ shell: bash
+ run: |
+ echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
+
+ - uses: actions/cache@v4
+ name: Setup pnpm cache
+ with:
+ path: ${{ env.STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build with VitePress
+ run: pnpm run docs:build
+
+ - name: Generate CNAME
+ run: echo "interview.doocs.org" > docs/.vitepress/dist/CNAME
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: docs/.vitepress/dist
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github_pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml
new file mode 100644
index 0000000..37c6004
--- /dev/null
+++ b/.github/workflows/prettier.yml
@@ -0,0 +1,25 @@
+name: Prettier
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ prettier:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ ref: ${{ github.head_ref }}
+
+ - name: Prettify code
+ uses: creyD/prettier_action@v3.3
+ with:
+ prettier_options: --write **/*.{md}
+ commit_message: "style: prettify code"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 2664e7a..f7fe7c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,7 @@
hs_err_pid*
node_modules
-*.json
+
+
+docs/.vitepress/dist
+docs/.vitepress/cache
\ No newline at end of file
diff --git a/.nojekyll b/.nojekyll
deleted file mode 100644
index e69de29..0000000
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..cf04042
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,2 @@
+shamefully-hoist=true
+strict-peer-dependencies=false
diff --git a/LICENSE b/LICENSE
index eecb76b..3b7b82d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,22 +1,427 @@
-# Creative Commons Attribution-NonCommercial 4.0 International License
+Attribution-ShareAlike 4.0 International
-Disclaimer: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-nc/4.0/legalcode).
+=======================================================================
-You are free to:
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
-- Share — copy and redistribute the material in any medium or format
-- Adapt — remix, transform, and build upon the material
+Using Creative Commons Public Licenses
-The licensor cannot revoke these freedoms as long as you follow the license terms.
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
-Under the following terms:
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
-- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
-- NonCommercial — You may not use the material for commercial purposes.
-- No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
-Notices:
+=======================================================================
-You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
-No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/Main.java b/Main.java
new file mode 100644
index 0000000..99abdd7
--- /dev/null
+++ b/Main.java
@@ -0,0 +1,8 @@
+/**
+ * @author yanglbme
+ */
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("互联网公司 IT 技术面试题集");
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index c6d9df6..76fed1c 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,86 @@
# 互联网公司 IT 技术面试题集
-[](https://github.com/doocs/coding-interview/stargazers)
+
+[](https://github.com/doocs/coding-interview/blob/main/LICENSE)
+[](https://github.com/doocs/coding-interview/stargazers)
+[](https://github.com/doocs/coding-interview/issues)
[](https://github.com/doocs/coding-interview/network/members)
-[](https://github.com/doocs/coding-interview/issues)
-[](http://makeapullrequest.com)
+[](http://makeapullrequest.com)
+
+## 项目介绍
-## Introduction
本仓库用于记录各大互联网公司 IT 技术面试高频题以及经典书籍读书笔记,包括《剑指 Offer》、《编程之美》、《代码整洁之道》等,抽空更新中。
-## Notes
+## 站点
+
+https://interview.doocs.org
+
+## 书籍笔记
+
### [《剑指 Offer》](/docs/coding-interview.md)
+
这本书选取的[题目](/docs/coding-interview.md)都是被各大公司面试官反复采用的编程题,极具实战意义。当然,如果一开始觉得这些题目比较难,也是很正常的,因为大公司的面试本身就不简单。我们逐步掌握书中总结的解题方法之后,再去面试大公司,将会轻松很多。
-推荐两个在线刷《剑指 Offer》的平台,个人比较倾向于 [AcWing](https://www.acwing.com) 。
+推荐三个在线刷《剑指 Offer》的平台,个人比较倾向于 [LeetCode 中国](https://leetcode.cn/problemset/lcof/) 。
-- [AcWing](https://www.acwing.com/problem/):墙裂推荐,支持 Java11。团队成员来自北大清华,剑指 Offer 第二版题目都有。
+- [LeetCode 中国](https://leetcode.cn/problemset/lcof/):近期上线,支持多种编程语言,墙裂推荐。
+- [AcWing](https://www.acwing.com/problem/):支持 Java11。团队成员来自北大清华,剑指 Offer 第二版题目都有。
- [NowCoder](https://www.nowcoder.com/ta/coding-interviews):这个应该大多数人都知道,但是没有剑指 Offer 第二版新增的题目。
### [《代码整洁之道》](/docs/clean-code.md)
-这本书名为 *Clean Code*,乃是 Object Mentor(鲍勃大叔开办的技术咨询和培训公司)一干大牛在编程方面的经验累积。写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。
+
+这本书名为 _Clean Code_,乃是 Object Mentor(鲍勃大叔开办的技术咨询和培训公司)一干大牛在编程方面的经验累积。写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。
作者 Robert C. Martin 在书中阐述了代码各个方面如何做到整洁的经验与最佳实践。我们若能长期遵照这些经验编写代码,所谓“代码感”也就自然而然滋生出来。
### [《阿里巴巴 Java 开发手册》](/docs/effective-coding.md)
+
别人都说我们是搬砖的码农,但我们知道自己是追求个性的艺术家。也许我们不会过多在意自己的外表和穿着,但在我们不羁的外表下,骨子里追求着代码的美、系统的美、设计的美,代码规范其实就是一个对程序美的定义。
-## Reading List
-- [x] 《剑指 Offer》 355/355
-- [x] 《代码整洁之道》 296/296
-- [x] 《Effective Coding——阿里巴巴 Java 开发手册》 96/96
-- [ ] 《Effective Java 第二版》 阅读中...
-- [ ] 《Redis 实战》
-- [ ] 《Redis 设计与实现》
-- [ ] 《Java 工程师修炼之道》 0/352
-- [ ] 《编程珠玑第二版》
-- [ ] 《编程之美:微软技术面试心得》 57/382
-- [ ] 《程序员代码面试指南——IT名企算法与数据结构题目最优解》
-- [ ] 《Java 程序员面试笔试宝典》 67/349
-- [ ] 《程序员面试金典》
-- [ ] 《实战 Java 高并发程序设计》
-- [ ] 《深入分析 Java Web 技术内幕》
-- [ ] 《大型网站技术架构——核心原理与案例分析》
-- [ ] 《高性能 MySQL》
-- [ ] 《图解 HTTP》
-- [ ] 《深入理解 Java 虚拟机》
-- [ ] 《Java 编程思想》
-- [ ] 《鸟哥的 Linux 私房菜》
\ No newline at end of file
+### [《枕边算法书》](/docs/algorithm-stories.md)
+
+这本书,我是把它当作一本故事书来读的,里面的部分知识点还挺有意思。
+
+### [《Effective Java》](/docs/effective-java.md)
+
+这本书共包含了 78 个条目,每个条目讨论一条规则。它适用于任何具有实际 Java 工作经验的工程师,对于高级工程师,也能够提供一些发人深思的东西,是所有 Java 工程师必读书籍之一。
+
+---
+
+## Doocs 社区优质项目
+
+Doocs 技术社区,致力于打造一个内容完整、持续成长的互联网开发者学习生态圈!以下是 Doocs 旗下的一些优秀项目,欢迎各位开发者朋友持续保持关注。
+
+| # | 项目 | 描述 | 热度 |
+| --- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | [advanced-java](https://github.com/doocs/advanced-java) | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 |   |
+| 2 | [leetcode](https://github.com/doocs/leetcode) | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 |   |
+| 3 | [source-code-hunter](https://github.com/doocs/source-code-hunter) | 互联网常用组件框架源码分析。 |   |
+| 4 | [jvm](https://github.com/doocs/jvm) | Java 虚拟机底层原理知识总结。 |   |
+| 5 | [coding-interview](https://github.com/doocs/coding-interview) | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 |   |
+| 6 | [md](https://github.com/doocs/md) | 一款高度简洁的微信 Markdown 编辑器。 |   |
+| 7 | [technical-books](https://github.com/doocs/technical-books) | 值得一看的技术书籍列表。 |   |
+
+## 贡献者
+
+感谢以下所有朋友对 [Doocs 技术社区](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。
+
+
+
+
+
+
+
+## 公众号
+
+[Doocs](https://github.com/doocs) 技术社区旗下唯一公众号「**Doocs**」,欢迎扫码关注,**专注分享技术领域相关知识及行业最新资讯**。当然,也可以加我个人微信(备注:GitHub),拉你进技术交流群。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_coverpage.md b/_coverpage.md
deleted file mode 100644
index bf9f6a8..0000000
--- a/_coverpage.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-# Coding-Interview
-
-> 代码面试题集,包括剑指 Offer、编程之美等。
-
-* 记录各大互联网公司 IT 技术面试高频题
-
-[GitHub](https://github.com/doocs/coding-interview/)
-[Get Started](#互联网公司-it-技术面试题集)
\ No newline at end of file
diff --git a/_navbar.md b/_navbar.md
deleted file mode 100644
index f86a810..0000000
--- a/_navbar.md
+++ /dev/null
@@ -1,12 +0,0 @@
-* 题解
- * [剑指Offer](docs/coding-interview.md)
- * [编程之美](docs/the-beauty-of-programming.md)
-
-* 笔记
- * [代码整洁之道](docs/clean-code.md)
- * [阿里巴巴 Java 开发手册](docs/effective-coding.md)
-
-* 页面
- * [封面]()
- * [首页](README)
- * [GitHub](https://github.com/yanglbme)
\ No newline at end of file
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
new file mode 100644
index 0000000..be48126
--- /dev/null
+++ b/docs/.vitepress/config.mts
@@ -0,0 +1,69 @@
+import { defineConfig } from 'vitepress'
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ title: "coding-interview",
+ description: "互联网公司 IT 技术面试题集",
+ themeConfig: {
+ // https://vitepress.dev/reference/default-theme-config
+ nav: [
+ { text: '首页', link: '/' },
+ { text: '题解', link: '/coding-interview' },
+ { text: '编程之美', link: '/the-beauty-of-programming' },
+ { text: '代码整洁之道', link: '/clean-code' },
+ { text: '阿里巴巴 Java 开发手册', link: '/effective-coding' },
+ { text: '枕边算法书', link: '/algorithm-stories' },
+ { text: 'Effective Java', link: '/effective-java' }
+ ],
+ search: {
+ provider: 'local'
+ },
+ logo: '/favicon-32x32.png',
+ footer: {
+ message: 'Released under the CC-BY-SA-4.0 license.',
+ copyright: `版权所有 © 2018-${new Date().getFullYear()} Doocs `
+ },
+ docFooter: {
+ prev: '上一篇',
+ next: '下一篇'
+ },
+ editLink: {
+ pattern: 'https://github.com/doocs/coding-interview/edit/main/docs/:path',
+ text: '在 GitHub 编辑'
+ },
+ sidebar: [
+ {
+ text: '📚 题解',
+ items: [
+ { text: '剑指 Offer', link: '/coding-interview' },
+ { text: '编程之美', link: '/the-beauty-of-programming' }
+ ]
+ },
+ {
+ text: '📝 代码整洁',
+ items: [
+ { text: '代码整洁之道', link: '/clean-code' },
+ { text: '阿里巴巴 Java 开发手册', link: '/effective-coding' }
+ ]
+ },
+ {
+ text: '📖 其他书籍',
+ items: [
+ { text: '枕边算法书', link: '/algorithm-stories' },
+ { text: 'Effective Java', link: '/effective-java' }
+ ]
+ }
+ ],
+
+ socialLinks: [
+ { icon: 'github', link: 'https://github.com/doocs/coding-interview' }
+ ]
+ },
+ head: [
+ ['link', { rel: 'icon', type: 'image/png', href: '/favicon-32x32.png' }],
+ ],
+ cleanUrls: true,
+ sitemap: {
+ hostname: 'https://interview.doocs.org'
+ }
+})
diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue
new file mode 100644
index 0000000..87df4f7
--- /dev/null
+++ b/docs/.vitepress/theme/Layout.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js
new file mode 100644
index 0000000..0c95b59
--- /dev/null
+++ b/docs/.vitepress/theme/index.js
@@ -0,0 +1,38 @@
+import DefaultTheme from "vitepress/theme";
+import giscusTalk from "vitepress-plugin-comment-with-giscus";
+import { useData, useRoute } from "vitepress";
+import { toRefs } from "vue";
+import Layout from "./Layout.vue";
+
+export default {
+ extends: DefaultTheme,
+ Layout: Layout,
+ enhanceApp(ctx) {
+ DefaultTheme.enhanceApp(ctx);
+ },
+ setup() {
+ const { frontmatter } = toRefs(useData());
+ const route = useRoute();
+
+ giscusTalk(
+ {
+ repo: "doocs/coding-interview",
+ repoId: "MDEwOlJlcG9zaXRvcnkxNTQ5MTMxODI=",
+ mapping: "pathname",
+ category: "Announcements",
+ categoryId: "IC_kwDOCTvJns4CpHBe",
+ inputPosition: "top",
+ lang: "zh-CN",
+ homePageShowComment: true,
+ loading: "lazying",
+ lightTheme: "light",
+ darkTheme: "transparent_dark",
+ },
+ {
+ frontmatter,
+ route,
+ },
+ true
+ );
+ },
+};
\ No newline at end of file
diff --git a/docs/algorithm-stories.md b/docs/algorithm-stories.md
new file mode 100644
index 0000000..6bd1c9b
--- /dev/null
+++ b/docs/algorithm-stories.md
@@ -0,0 +1,166 @@
+# 《枕边算法书》
+
+> 这里仅挑一些有意思的故事或知识点做个记录。
+
+- [“红色眼睛与褐色眼睛”谜题](#红色眼睛与褐色眼睛谜题)
+- [找出剩下的一个数](#找出剩下的一个数)
+- [说出 2199 年 7 月 2 日是星期几](#说出2199年7月2日是星期几)
+- [梅森素数](#梅森素数)
+- [杯中的水是否超过一半](#杯中的水是否超过一半)
+
+## “红色眼睛与褐色眼睛”谜题
+
+从前,有个小岛上只住着和尚。有些和尚的眼睛是红色的,而另一些则是褐色的。红色眼睛的和尚受到诅咒,如果得知自己的眼睛是红色的,那么当晚 12 点必须自行了断,无一例外。
+
+和尚间有一条不成文的规定,就是彼此不能提起对方眼睛的颜色。小岛上没有一面镜子,也没有任何可以反射自己容貌的物体。因此,没有任何一个和尚能够得知自己眼睛的颜色。出于这些原因,每个和尚都过着幸福的日子。
+
+有一天,岛上突然来了一位游客,她完全处于状况外。于是,她对和尚们说:“你们当中至少有一位的眼睛是红色的”。
+
+
+
+这名无心的游客当天就离开了小岛,而和尚们却因第一次听到有关眼睛颜色的话题而惴惴不安。当晚,小岛上开始出现了可怕的事情......
+
+究竟是什么事呢?
+
+这道题不简单却非常有意思,而一旦知道答案,又会觉得并不太难。这并非是那种荒谬的问题,要想解开需要一些逻辑推理,所以不要试图一下子解开。先花 2 分钟时间独立思考一下吧。
+
+```
+if ((思考时间 > 2 分钟) || (已经知道答案了吗)) {
+ 跳转至下一段
+} else {
+ 返回上一段,并至少思考 2 分钟
+}
+```
+
+下面开始查看正确答案。
+
+游客说,“至少有一个人”的眼睛是红色的。假如这岛上**没有任何一个和尚的眼睛是红色的**,那么这会导致最糟糕的结果。你想一想,对于和尚们来说,除了自己以外,看到的其它和尚的眼睛都是褐色的。因此,每个和尚都会认为自己的眼睛是红色的,可想而知,所有和尚当晚都会自杀。
+
+如果**只有一名和尚的眼睛是红色的**,会出现什么结果呢?很简单,这名和尚知道其它和尚眼睛都是褐色的,那么就会判断出自己眼睛的颜色,进而选择自杀。游客的无心之言就这样夺走了一条生命。
+
+考虑稍微复杂点的情况。假如**有两个红眼和尚**,那么他们各自都知道有一个红眼和尚,都以为说的是对方。这两个和尚心想:“那个红眼的家伙今晚就要自杀喽。”当晚,各自都安心入睡了。第二天,这两个和尚相互碰面,并看到对方没有自杀时,心理备受打击。他们都会意识到,红眼和尚有两个而非一个,而另一个正是自己。除此之外的任何情况都不可能让对方在第一个晚上不自杀而安然入睡。因此,受到极大打击的这两个红眼和尚在第二天晚上**都会悲惨死去**。
+
+再考虑更复杂的情况。如果有 3 个红眼和尚,又会是怎样呢?平时,这 3 位会看到两个红眼和尚,所以听到游客的话后,都不会选择自杀。第一晚过后,他们又会想,另外两个和尚在第二天晚上都会自杀(就是前面探讨的“有两个红眼和尚”的情形)。到了第三天早上,看到本以为会自杀的另两个和尚并没有自杀时,根本没想到自己也是红眼和尚的这 3 人会同时受到极大的打击。因为,两个红眼和尚第二天晚上也没有自杀,这表明还有一个红眼和尚,而这第三个红眼和尚正是自己。
+
+这种逻辑会反复循环。因此,该题的答案是“若小岛上共有 n 个红眼和尚,那么第 n 个晚上这些和尚会同时自杀”。例如,小岛上共有 5 个红眼和尚,那么第 5 个晚上,这 5 个红眼和尚会同时自杀。
+
+这道题其实可以利用递归的方法。假设红眼和尚人数 N 为 10,那么我们可以适用 N 为 9 的逻辑。同理,N 为 8 或 7 时,都适用 `N-1` 时的逻辑。将 `N=1`,即 “只有一个红眼和尚” 视为终止条件,即可得出最终结果。这种过程与计算机算法中函数的递归调用过程完全相同。
+
+## 找出剩下的一个数
+
+有一个能保存 99 个数值的数组 `item[0], item[1],...item[98]`。从拥有 `1~100` 元素的集合 {1,2,3,...,100} 中,随机抽取 99 个元素保存到数组。集合中共有 100 个元素,而数组只能保存 99 个数值,所以集合中会剩下一个元素。编写程序,找出最后剩下的数。
+
+还是先花 2 分钟想一想吧。
+
+好了,这个问题其实非常简单,但没能正确理解题意的读者可能认为很难。答案如下代码所示。
+
+```java
+int res = 5050;
+for (int i = 0; i < 99; ++i) {
+ res -= item[i];
+}
+System.out.println("最后剩下的数是:" + res);
+```
+
+如果将集合的 100 个数值累加,会得到 5050。依次从 5050 减去数组中的 99 个数值,最后的数就是没能保存到数组的那个剩余数值。也许很多读者想到了与此相近的算法。即使没有得到正确答案也不用失望,因为真正应该感到失望的人是那些没能找到答案后轻易选择放弃、想要直接查看正确答案的人。
+
+## 说出 2199 年 7 月 2 日是星期几
+
+先公布答案吧,2199 年 7 月 2 日是星期二。其实可以靠运气蒙一下,准确率是 1/7。要想真正求出正确答案,过程并不简单。也许有些读者会自己设计精妙算法求出正确答案,但我还是想通过约翰•康威教授的“末日”算法进行说明。
+
+末日算法虽然不是“游戏”,但在聚会中能够引起初次见面的异性的好奇。因此,为不少“花花公子”踏入数学殿堂做出了很大贡献。例如,“美丽的女士,请告诉我您的生日,让我猜猜是星期几。” “请您随便说一个年份,我会猜出当年的情人节是星期几”。虽然听起来比较肉麻,不过这样就能一下子吸引对方的注意。
+
+康威教授的末日算法执行环境就是我们今天使用的“**公历**”环境。
+
+首先,先理清楚**什么是闰年**。闰年是年份能被 4 整除但不能被 100 整除,或者能被 400 整除的年份。闰年 2 月有 29 天,而平年 2 月是 28 天。
+
+```java
+// 判断是否是闰年
+boolean isLeapYear(int year) {
+ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+}
+```
+
+康威末日算法的运行原理非常简单。为了判断不同日期的星期,算法中首先**设立一个必要的“基准”**。然后,**根据星期以 7 为循环的原则和对闰年的考虑**,计算日期对应的星期。
+
+平年时,将 2.28 日设置为“末日”;到了闰年,将 2.29 日设置为“末日”。只要知道了特殊年份(e.g. 1900 年) “末日”的星期,那么根据康威算法即可判断其它日期的星期。
+
+我们都知道,星期以 7 为循环,所以与“末日”以 7 的倍数为间隔的日期就和“末日”具有相同的星期。利用这个原理,先记住每个月中总是与“末日”星期相同的一个日期,即可快速算出结果。
+
+每个月与“末日”具有相同星期的一天分别是:
+
+```
+4.4、6.6、8.8、10.10、12.12、9.5、5.9、7.11、11.7、3.7
+```
+
+只需要记住 4、6、8、10、12 这几个月与日的数字相同,然后是 9.5、5.9、7.11、11.7,这几个是对称的,还有一个是 3.7。是不是很容易记住?
+
+好了,那么我们**只要知道当年的“末日”是星期几,就可以推算出当年的任何一天是星期几了**。
+
+举个例子吧。2003 年的“末日”是星期五,我们推算一下那一年的圣诞节的星期。由于 2003 年“末日”是星期五,所以 12 月 12 日也是星期五(我们上面记住了每个月与“末日”具有相同星期的一天),那么 `12+7*2=26`,12 月 26 日也是星期五,所以 12 月 25 日是星期四。
+
+那么问题来了,**怎么才能知道某一年的“末日”是星期几呢**?
+
+这种情况下,需要记住“末日”的星期每跨 1 年就会加 1,若遇到闰年就会加 2。
+
+例如,1900 年的“末日”是星期三,那么 1901 年的“末日”是星期四(+1),1902 年的“末日”是星期五(+1),1903 年的“末日”是星期六(+1),而 1904 年(闰年)的“末日”是“星期一”(+2)。
+
+就是说,我们记住了 1900 年“末日”是星期三,就可以推算出其它年份的“末日”是星期几了。
+
+这样一个个推算还是很麻烦,可能一不小心就推错了。为此,康威教授贴心地给我们提供了如下形式的列表。
+
+```
+6, 11.5, 17, 23, 28, 34, 39.5, 45, 51, 56, 62, 67.5, 73, 79, 84, 90, 95.5
+```
+
+就是说,1900 年“末日”是星期三,那么 1906,1917,1923... “末日”也是星期三, 11.5 表示 1911 年的“末日”是星期二(-1),而 1912 年的“末日”是星期四(+1)。记住这个列表,我们就能够算出所有 20 世纪年份的“末日基准”了。
+
+如果一个美丽的姑娘说“我的生日是 1992.9.13” 时,我们可以马上说出当天的星期。既然康威列表有 90 这个数字,表示 1990 年的“末日”也是星期三,那么 1901 年(平年)“末日”是星期四(+1),1902 年(闰年)“末日”是星期六(+2),所以 9.5/9.12 也是星期六,1992.9.13 就是星期日。
+
+不过,**年份跨越世纪时,康威列表就会失去作用**。
+
+题目中问的是 2199.7.2 的星期,如果不能得知 2199 年“末日”是星期几,那么这道题很难求解。对于不同世纪的年份,没有什么特别的方法能够猜出“末日”的星期。只能将被 100 整除的年份表示为日历形式时,从中得到一些规律而已。
+
+| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
+| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
+| 1599 | | 1600 | 1601 | 1602 | | |
+| 1700 | 1701 | 1702 | 1703 | | 1704 | 1705 |
+| | 1796 | 1797 | 1798 | 1799 | 1800 | 1801 |
+| 1897 | 1898 | 1899 | 1900 | 1901 | 1902 | 1903 |
+| 1999 | | | 2000 | 2001 | 2002 | 2003 |
+| 2100 | 2101 | 2102 | 2103 | | 2104 | 2105 |
+| | 2196 | 2197 | 2198 | 2199 | 2200 | 2201 |
+
+这道题看似简单,但其实不仅需要了解“末日”算法,还需要深入了解上述模式。上面的日历中,2199 年的“末日”是星期四,所以 2199.7.11/2199.7.4 也是星期四,所以 2199.7.2 是星期二。
+
+感受到康威教授末日算法的精妙之处了吧。
+
+## 梅森素数
+
+马林•梅森是法国哲学家、修道士。16 世纪,数论领域存在着一个错误的假设,而一直被认为是事实。根据这个假设,对所有素数 p,2p -1 也是素数。将素数 2,5,7 带入,结果均为负数。
+
+从直观角度看,对素数 p,总有 2p -1 也是素数的假设成立。不过,仅仅通过几个结果就想判断命题真伪,这在数学中是最“无知”的行为。这种代入几个变量进行的测试往往以程序能够正常运行的“晴天”作为前提条件,如果遇到“雨天”,这种只经过松散测试的程序会发生很多意想不到的问题。算法的内部逻辑应该紧凑,不给 Bug 任何可乘之机。
+
+后来,人们最终证明,p 为素数时,2p -1 的结果不一定是素数。虽然如此,有些人还是好奇,p 是什么样的素数时,2p -1 结果将为素数。为了解答这种好奇,梅森在 1644 年发表的论文里提出了如下主张:
+
+> “若 p 为 2、3、5、7、13、17、19、31、67、127、257 之一,那么 2p -1 的结果是素数。”
+
+梅森一直希望将存在的所有素数都表示为 2p -1 这种短小而精简的公式形式。若真能找到那样一个公式,将是美丽得让人窒息的、绝妙的数学发现。不过,梅森的梦想没能实现。
+
+随着时间的流逝,后世数学家们通过计算得出,应当删除梅森假设中的 67 和 257,而可以添加 61、89、107。就这样,从前简洁而“有理”的命题 “若 p 是素数,则 2p -1 也是素数” 已消失不见,而留下的 “p 为某值时,结果为素数,否则不是素数”等杂乱的 if-else 语句正让算法变得越来越杂乱不堪。
+
+实际编程中,如果越来越复杂的 `if-else` 语句影响程序简洁性,那么到了某一时刻,程序员就会考虑“重构”,对于算法也是一样。后来,人们将精简的新算法献给一生都在祈祷和学习的修道士梅森:
+
+> “如果 p 为素数时 2p -1 也是素数,那么此素数为梅森素数。”
+
+## 杯中的水是否超过一半
+
+空房间中有个圆柱形水杯,杯口和杯底直径相同,里面有半杯左右的水。找出方法,判断杯中水超过一半还是不到一半。空荡荡的房间中没有任何可使用的器具或工具。
+
+答案本身非常简单,不过能够真正求解的人却寥寥无几。想问题的时候,请不要考虑房间或水的温度,以及化学反应等“不讲理”的方法。另外,不允许喝杯子里的水。
+
+
+
+即使读完题没能马上想起答案,但看到插图后能够立刻明白,也可以说很有编程的感觉。将杯子倾斜,使水面刚好到达杯口时,查看杯底的水就能得出答案了。
+
+算法的编写与之大体相同。各位因为找不到突破口而郁闷时,甚至会怀疑给出的问题究竟有没有解。然而找到突破口后,再回首会发现,原来解决之道竟如此简单。
diff --git a/docs/clean-code.md b/docs/clean-code.md
index ceb5598..834eaee 100644
--- a/docs/clean-code.md
+++ b/docs/clean-code.md
@@ -1,17 +1,21 @@
# 《代码整洁之道》
+
## 代码猴子与童子军军规
+
1. 我们就像一群代码猴子,上蹿下跳,自以为领略了编程的真谛。可惜,当我们抓着几个酸桃子,得意洋洋坐到树枝上,却对自己造成的混乱熟视无睹。那堆“可以运行”的乱麻程序,就在我们的眼皮底下慢慢腐坏。
## 第一章 整洁代码
+
1. 勒布朗法则:稍后等于永不(Later equals never)。
1. 制造混乱无助于赶上期限。混乱只会立刻拖慢你,叫你错过期限。赶上期限的唯一方法——做的快的唯一方法——就是始终尽可能保持代码整洁。
1. Javadoc 中的 `@author` 字段告诉我们自己是什么人。我们是作者,作者都有读者。实际上,作者有责任与读者做良好沟通。下次你写代码的时候,记得自己是作者,要为评判你工作的读者写代码。
## 第三章 函数
+
1. 函数的第一规则是要短小。第二条规则是还要更短小...经过漫长的试错,经验告诉我,函数就该小。
1. 函数应该做一件事。做好这件事。只做这一件事。判断函数是否不止做了一件事,有一个方法,就是看是否能再拆出一个函数,该函数不仅只是单纯地重新诠释其实现。
1. 要确保函数只做一件事,函数中的语句都要在同一抽象层级上。函数中混杂不同的抽象层级,往往让人迷惑。读者可能无法判断某个表达式是基础概念还是细节。更恶劣的是,就像破损的窗户,一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。
-1. 写出短小的 `switch` 语句往往很难。写出只做一件事的 `switch` 语句也很难。我们总不发避开 `switch` 语句,不过还是能够确保 `switch` 都埋藏在较低的抽象层级,而且永远不重复。
+1. 写出短小的 `switch` 语句往往很难。写出只做一件事的 `switch` 语句也很难。我们总无法避开 `switch` 语句,不过还是能够确保 `switch` 都埋藏在较低的抽象层级,而且永远不重复。
1. 最理想的参数数量是零,其次是一,再次是二...从测试的角度看,参数甚至更叫人为难。想想看,要编写能确保参数的各种组合运行正常的测试用例,是多么困难的事。如果没有参数,就是小菜一碟。
1. 函数承诺只做一件事,但还是会做其它被藏起来的事。有时,它会对自己类中的变量做出未能预期的改动,导致古怪的时序性耦合及顺序依赖。
@@ -37,87 +41,100 @@ public class UserValidator {
副作用就在于对 `Session.initialize()` 的调用。`checkPassword` 函数是用来检查密码的。该名称未暗示它会初始化该次会话。当某个误信了函数名的调用者想要检查用户有效性时,就得冒着抹除现有会话数据的风险。这一副作用造成了一次时序性耦合。也就是说,`checkPassword` 只能在特定时刻调用。
## 第四章 注释
+
1. 注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。注释总是一种失败。我们总无法找到不用注释就能表达自我的方法,所以总要有注释,这并不值得庆贺。
2. 如果你发现自己需要写注释,再想想看是否有办法翻盘,用代码来表达。
-1. 有时,有理由用 `// TODO` 形式在源代码中放置要做的工作列表。`TODO` 是一种程序员认为应该做,但由于某些原因目前还没做的工作。
-1. 没有什么比被良好描述的公共 API 更有用和令人满意的了。如果你在编写公共 API,就该为它编写良好的 Javadoc。
-1. 删掉无用而多余的 Javadoc 吧,这些注释只是一味将代码搞得含糊不明,完全没有文档上的价值。
-1. 所谓每个函数都要有 Javadoc 或每个变量都要有注释的规矩全然是愚蠢可笑的。这类注释徒然让代码变得散乱,满口胡言,令人迷惑不解。
-1. 20 世纪 60 年代,曾经有那么一段时间,注释掉的代码可能有用。但我们已经拥有优良的源代码控制系统如此之久,这些系统可以为我们记住不要的代码。我们无需再用注释来标记,删掉即可,它们丢不了,我担保。
+3. 有时,有理由用 `// TODO` 形式在源代码中放置要做的工作列表。`TODO` 是一种程序员认为应该做,但由于某些原因目前还没做的工作。
+4. 没有什么比被良好描述的公共 API 更有用和令人满意的了。如果你在编写公共 API,就该为它编写良好的 Javadoc。
+5. 删掉无用而多余的 Javadoc 吧,这些注释只是一味将代码搞得含糊不明,完全没有文档上的价值。
+6. 所谓每个函数都要有 Javadoc 或每个变量都要有注释的规矩全然是愚蠢可笑的。这类注释徒然让代码变得散乱,满口胡言,令人迷惑不解。
+7. 20 世纪 60 年代,曾经有那么一段时间,注释掉的代码可能有用。但我们已经拥有优良的源代码控制系统如此之久,这些系统可以为我们记住不要的代码。我们无需再用注释来标记,删掉即可,它们丢不了,我担保。
## 第五章 格式
+
1. 代码格式很重要,必须严肃对待。代码格式关乎沟通,而沟通是专业开发者的头等大事。
1. 你今天编写的功能,极有可能在下一版本中被修改,但代码的可读性却会对以后可能发生的修改行为产生深远影响。原始代码修改之后很久,其代码风格和可读性仍会影响到可维护性和扩展性。即便代码不复存在,你的风格和律条仍会存活下来。
1. 若某个函数调用了另外一个,就应该把它们放在一起,而且调用者应该尽可能放在被调用者上面。
## 第六章 对象和数据结构
+
1. 最为精炼的数据结构,是一个只有公共变量、没有函数的类。这种数据结构有时被称为数据传送对象,或 `DTO`(Data Transfer Objects)。DTO 是非常有用的结构,尤其是在于数据库通信、或解析套接字传输的消息之类的场景中。
## 第七章 使用异常而非返回码
+
1. 在很久以前,许多语言都不支持异常。这些语言处理和汇报错误的手段都有限。你要么设置一个错误标识,要么返回给调用者检查的错误码。这类手段的问题在于,它们搞乱了调用者代码。调用者必须在调用之后即可检查错误。不幸的是,这个步骤很容易被遗忘。最好是抛出一个异常,这样其逻辑不会被错误处理搞乱。
1. 使用不可控异常。可控异常 `checked exception` 的代价是违反开闭原则。如果你在方法中抛出可控异常,而 catch 语句在三个层级之上,你就得在 catch 语句和抛出异常处之间的每个方法签名中声明该异常。这意味着对软件中低层级的修改,都将涉及较高层级的签名。最终得到的就是一个从软件最底端贯穿到最高端的修改链。
1. 别返回 null 值。我不想去计算曾经见过多少每行代码都在检查 null 值的应用程序。Java 中有 `Colletions.emptyList()` 方法,该方法返回一个预定义不可变列表,这样编码,就能尽量避免 `NullPointerException` 的出现,代码也就更整洁了。
1. 别传递 null 值。在大多数编程语言中,没有良好的方法能对付由调用者意外传入 null 值。事已如此,恰当的做法就是禁止传入 null 值。
## 第八章 边界
+
1. 第三方代码帮助我们在更少时间内发布更丰富的功能。在利用第三方程序包时,该从何处入手呢?我们没有测试第三方代码的职责,但为要使用的第三方代码编写测试,可能最符合我们的利益。
1. 学习第三方代码很难,整合第三方代码也很难,同时做这两件事难上加难。不要在生产代码中试验新东西,而是编写测试来遍览和理解第三方代码,这叫“学习性测试”。
## 第九章 单元测试
+
1. TDD 三定律:
- - **定律一** 在编写不能通过的单元测试前,不可编写生产代码。
- - **定律二** 只可编写刚好无法通过的单元测试,不能编译也算不通过。
- - **定律三** 只可编写刚好足以通过当前失败测试的生产代码。
+ - **定律一** 在编写不能通过的单元测试前,不可编写生产代码。
+ - **定律二** 只可编写刚好无法通过的单元测试,不能编译也算不通过。
+ - **定律三** 只可编写刚好足以通过当前失败测试的生产代码。
1. TDD 三定律其实说的是,先写失败的 Case,写完之后才开始写功能 Code,只要 Code 通过了 Case,就不要再写功能代码了。也就是说,写完一个测试,就要写对应的生产代码。
1. 测试代码和生产代码一样重要。它可不是二等公民。它需要被思考、被设计和被照料。它该像生产代码一般保持整洁。
1. 如果测试不能保持整洁,你就会失去它们。没有了测试,你就会失去保证生产代码可扩展的一切要素。有了测试,你就不担心对代码的修改!没有测试,每次修改都可能带来缺陷。
1. 覆盖了生产代码的自动化单元测试程序组能尽可能地保持设计和架构的整洁。测试带来了一切好处,因为测试使改动变得可能。
1. 整洁的测试有三个要素:可读性、可读性、可读性。测试应该明确、简洁,还有足够的表达力。在测试中,要以尽量少的文字表达大量的内容。
1. F.I.R.S.T 规则:
- - **Fast**(快速) 测试应该能快速运行。测试运行缓慢,你就不会想要频繁地运行它。如果你不频繁运行测试,就不能尽早发现问题,也无法轻易修正。
- - **Independent**(独立) 测试应该相互独立。某个测试不应为下一个测试设定条件。你应该可以单独运行每个测试,及以任何顺序运行测试。
- - **Repeatable**(可重复) 测试应当可在任何环境中重复通过。
- - **Self-Validating** (自足验证) 测试应该有布尔值输出。
- - **Timely**(及时) 测试应及时编写。单元测试应该恰好在使其通过的生产代码之前编写。
+ - **Fast**(快速) 测试应该能快速运行。测试运行缓慢,你就不会想要频繁地运行它。如果你不频繁运行测试,就不能尽早发现问题,也无法轻易修正。
+ - **Independent**(独立) 测试应该相互独立。某个测试不应为下一个测试设定条件。你应该可以单独运行每个测试,及以任何顺序运行测试。
+ - **Repeatable**(可重复) 测试应当可在任何环境中重复通过。
+ - **Self-Validating** (自足验证) 测试应该有布尔值输出。
+ - **Timely**(及时) 测试应及时编写。单元测试应该恰好在使其通过的生产代码之前编写。
## 第十章 类
+
1. 面向对象的其中一个设计原则是“开放——闭合原则”,即类应当对扩展开放,对修改封闭。我们希望将系统打造成在添加或修改特性时尽可能少惹麻烦的架子。在理想系统中,我们通过扩展系统而不是修改现有代码来添加新特性。
1. 类的另一条设计原则是“依赖倒置原则”(Dependency Inversion Principle, DIP),DIP 认为类应该依赖于抽象而不是依赖于具体细节。
## 第十一章 系统
+
1. 有一种强大的机制可以实现分离构造与使用,那就是依赖注入(Dependency Injection, DI),它是控制反转(Inversion of Control, IoC)在依赖管理中的一种应用手段。控制反转将第二权责从对象中拿出来,转移到另一个专注于此的对象中,从而遵循了**单一权责原则**。在依赖管理情境中,对象不应负责实体化对自身的依赖,而应当将这份权责移交给其它“有权力”的机制,从而实现控制的反转。
1. “一开始就做对系统”纯属神话。反之,我们应该只去实现今天的用户故事,然后重构,明天再扩展系统、实现新的用户故事。这就是迭代和增量敏捷的精髓所在。
## 第十二章 迭进
+
1. 简单设计的四条规则,按重要程度排序:
- - 运行所有测试;
- - 不可重复;
- - 表达了程序员的意图;
- - 尽可能减少类和方法的数量。
+ - 运行所有测试;
+ - 不可重复;
+ - 表达了程序员的意图;
+ - 尽可能减少类和方法的数量。
1. 全面测试并持续通过所有测试的系统,就是可测试的系统。看似浅显,但却重要。不可测试的系统同样不可验证。不可验证的系统,绝不应该部署。
1. 重复是拥有良好设计系统的大敌,它代表着额外的工作、额外的风险和额外且不必要的复杂度。要想创建整洁的系统,需要有消除重复的意愿。
1. 软件项目的主要成本在于长期维护。代码应当清晰地表达其作者的意图。作者把代码写得越清晰,其他人花在理解代码上的时间也就越少,从而减少缺陷,缩减维护成本。
1. 为了保持类和函数短小,我们可能会造出太多的细小类和方法。所以这条规则也主张函数和类的数量要少。我们的目标是在保持函数和类短小的同时,保持整个系统短小精悍。不过更重要的是测试、消除重复和表达力。
## 第十三章 并发编程
+
1. 并发是一种解耦策略。它帮助我们把**做什么**(目的)和**何时做**(时机)分解开。解耦目的与时机能明显地改进应用程序的吞吐量和结构。
1. 并发有时能改进性能,但只在多个线程或处理器之间能分享大量等待时间的时候管用,事情没那么简单。
1. 并发算法的设计有可能与单线程系统的设计极不相同。目的与时机的解耦往往对系统结构产生巨大影响。
1. 并发编程中的一些基础定义:
- - **限定资源**:并发环境中有着固定尺寸或数量的资源。
- - **互斥**:每一时刻仅有一个线程能访问共享数据或共享资源。
- - **线程饥饿**:一个或一组线程在很长时间内或永久被禁止。例如,总是让执行得快的线程先运行,加入执行得快得线程没完没了,则执行时间长的线程就会“饥饿”。
- - **死锁**:两个或多个线程互相等待执行结束。每个线程都拥有其它线程需要的资源,得不到其它线程拥有的资源,就无法终止。
- - **活锁**:执行次序一致的线程,每个都想要起步,但发现其它线程已经“在路上”。由于竞步的原因,线程会持续尝试起步,但在很长时间内却无法如愿,甚至永远无法启动。
+ - **限定资源**:并发环境中有着固定尺寸或数量的资源。
+ - **互斥**:每一时刻仅有一个线程能访问共享数据或共享资源。
+ - **线程饥饿**:一个或一组线程在很长时间内或永久被禁止。例如,总是让执行得快的线程先运行,加入执行得快得线程没完没了,则执行时间长的线程就会“饥饿”。
+ - **死锁**:两个或多个线程互相等待执行结束。每个线程都拥有其它线程需要的资源,得不到其它线程拥有的资源,就无法终止。
+ - **活锁**:执行次序一致的线程,每个都想要起步,但发现其它线程已经“在路上”。由于竞步的原因,线程会持续尝试起步,但在很长时间内却无法如愿,甚至永远无法启动。
## 第十四章 逐步改进
+
1. 代码能工作还不够,能工作的代码经常会严重崩溃。满足于仅仅让代码工作的程序员不够专业。他们会害怕没时间改进代码的结构和设计,我不敢苟同。没什么比糟糕的代码给开发项目带来更深远和长期的损害了。
1. 进度可以重订,需求可以重新定义,团队动态可以修正。糟糕的代码只会一直腐败发酵,无情地拖着团队的后腿。
1. 保持代码持续整洁和简单,永不让腐坏有机会开始。
## 第十五章 JUnit 框架
+
1. 成员变量的前缀可以删除。在现今的运行环境中,这类范围性编码纯属多余。
1. 条件判断应当封装起来,从而更清晰地表达代码的意图。可以拆解处一个方法,解释这个条件判断。
+
```java
public String compact(String message) {
if (expected == null || actual == null || areStringsEqual()) {
@@ -138,6 +155,7 @@ private boolean shouldNotCompact() {
```
## 第十七章 味道与启发
+
1. 让注释传达本该更好地在源代码控制系统、问题追踪系统或任何其它记录系统中保存的信息,是不恰当的。
1. 除函数签名之外什么也没说的 Javadoc,也是多余的。
1. 看到注释掉的代码,就删除它!别担心,源代码控制系统还会记得它。
@@ -146,4 +164,4 @@ private boolean shouldNotCompact() {
1. 特性依恋是 Martin Fowler 提出的代码味道之一。类的方法只应对其所属类中的变量和函数感兴趣,不该垂青其它类中的变量和函数。我们要消除特性依恋。
1. 用多态替代 if/else 或 switch/case。对于给定的选择类型,不应有多于一个 switch 语句。在那个 switch 语句中的多个 case,必须创建多态对象,取代系统中其它类似 switch 语句。
1. 用命名常量替代魔术数。
-1. 现在 enum 已经加入 java 语言了,放心用吧!别再用那个 `public static final int` 老花招。那样做 int 的意义就丧失了,而用 enum 则不然,因为它们隶属于有名称的枚举。
\ No newline at end of file
+1. 现在 enum 已经加入 java 语言了,放心用吧!别再用那个 `public static final int` 老花招。那样做 int 的意义就丧失了,而用 enum 则不然,因为它们隶属于有名称的枚举。
diff --git a/docs/coding-interview.md b/docs/coding-interview.md
index 2bbda49..3f0c4b7 100644
--- a/docs/coding-interview.md
+++ b/docs/coding-interview.md
@@ -1,7 +1,11 @@
# 《剑指 Offer》
+
## 3.1 找出数组中重复的数
+
来源:[AcWing](https://www.acwing.com/problem/content/14/)
-### 题目描述
+
+### 题目描述
+
给定一个长度为 `n` 的整数数组 `nums`,数组中所有的数字都在 `0∼n−1` 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
@@ -11,6 +15,7 @@
**注意**:如果某些数字不在 `0∼n−1` 的范围内,或数组中不包含重复数字,则返回 `-1`;
**样例**
+
```
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
@@ -18,51 +23,45 @@
```
### 解法
-#### 解法一
-排序后,顺序扫描,判断是否有重复,时间复杂度为 `O(n²)`。
-#### 解法二
-利用哈希表,遍历数组,如果哈希表中没有该元素,则存入哈希表中,否则返回重复的元素。时间复杂度为 `O(n)`,空间复杂度为 `O(n)`。
+从题目我们可以知道,数组长度为 n,所有数字都在 `0~n-1` 范围内。如果元素不重复,那么数组应该就是 `[0, 1, 2, ...n-1]`(假设给数组排完了序)。也就是说,递增排序后,数组中的元素值与其对应的下标应该是相同的,即下标为 0 的元素值也是 0,以此类推。
-#### 解法三
-长度为 `n`,元素的数值范围也为 `n`,如果没有重复元素,那么数组每个下标对应的值与下标相等。
+首先,我们可以遍历数组,若存在元素不在 `0~n-1` 的范围内,直接返回 -1。
-从头到尾遍历数组,当扫描到下标 `i` 的数字 `nums[i]`:
-- 如果等于 `i`,继续向下扫描;
-- 如果不等于 `i`,拿它与第 `nums[i]` 个数进行比较,如果相等,说明有重复值,返回 `nums[i]`。如果不相等,就把第 `i` 个数 和第 `nums[i]` 个数交换。重复这个比较交换的过程。
+接着,再次遍历数组,若下标 `i` 与对应元素 `nums[i]` 不同,即 `nums[i] != i`,我们应该把 `nums[i]` 这个元素交换到正确的位置 `nums[i]`上。交换前,先判断 `nums[i]` 与 `nums[nums[i]]` 这两个元素是否相同,相同说明存在重复元素,直接返回,否则进行 swap 交换。交换过后,我们需要再次判断 i 位置上的元素,因此,我们使用 while 循环。
-此算法时间复杂度为 `O(n)`,因为每个元素最多只要两次交换,就能确定位置(比如把 2 跟 5 交换,此时 2 在正确的位置,而 5 需要再交换一次就能跑到正确的位置)。空间复杂度为 `O(1)`。
+可对照下方代码实现,加深理解。
```java
class Solution {
-
+
/**
* 查找数组中的重复元素
- *
+ *
* @param nums 数组
* @return 其中一个重复的元素
*/
public int duplicateInArray(int[] nums) {
- if (nums == null || nums.length < 2) {
- return -1;
- }
-
int n = nums.length;
- for (int e : nums) {
- if (e < 0 || e > n - 1) {
+
+ // 若存在数组元素不在[0, n-1] 的范围内,直接返回-1
+ for (int num : nums) {
+ if (num < 0 || num >= n) {
return -1;
}
}
+
for (int i = 0; i < n; ++i) {
while (nums[i] != i) {
- int val = nums[nums[i]];
- if (nums[i] == val) {
- return val;
+ if (nums[i] == nums[nums[i]]) {
+ // 说明位置i与位置nums[i]上的元素相同,直接返回该重复元素
+ return nums[i];
}
swap(nums, i, nums[i]);
}
}
return -1;
+
}
private void swap(int[] nums, int i, int j) {
@@ -74,14 +73,17 @@ class Solution {
```
## 3.2 不修改数组找出重复的数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
### 题目描述
+
给定一个长度为 `n+1` 的数组 `nums`,数组中所有的数均在 `1∼n` 的范围内,其中 `n≥1`。
请找出数组中任意一个重复的数,但不能修改输入的数组。
**样例**
+
```
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
@@ -91,10 +93,13 @@ class Solution {
**思考题**:如果只能使用 `O(1)` 的额外空间,该怎么做呢?
### 解法
+
#### 解法一
+
创建长度为 `n+1` 的辅助数组,把原数组的元素复制到辅助数组中。如果原数组被复制的数是 `m`,则放到辅助数组第 `m` 个位置。这样很容易找出重复元素。空间复杂度为 `O(n)`。
#### 解法二
+
数组元素的取值范围是 `[1, n]`,对该范围对半划分,分成 `[1, middle]`, `[middle+1, n]`。计算数组中有多少个(count)元素落在 `[1, middle]` 区间内,如果 count 大于 middle-1+1,那么说明这个范围内有重复元素,否则在另一个范围内。继续对这个范围对半划分,继续统计区间内元素数量。
时间复杂度 `O(n * log n)`,空间复杂度 `O(1)`。
@@ -155,13 +160,17 @@ class Solution {
```
## 4 二维数组中的查找
+
来源:[AcWing](https://www.acwing.com/problem/content/16/)
+
### 题目描述
+
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
**样例**
+
```
输入数组:
@@ -178,7 +187,9 @@ class Solution {
```
### 解法
+
从二维数组的右上方开始查找:
+
- 若元素值等于 `target`,返回 `true`;
- 若元素值大于 `target`,砍掉这一列,即 `--j`;
- 若元素值小于 `target`,砍掉这一行,即 `++i`。
@@ -219,14 +230,18 @@ class Solution {
```
## 5 替换空格
+
来源:[AcWing](https://www.acwing.com/problem/content/17/)
+
### 题目描述
+
请实现一个函数,把字符串中的每个空格替换成 `"%20"`。
你可以假定输入字符串的长度最大是 `1000`。
注意输出字符串的长度可能大于 `1000`。
**样例**
+
```
输入:"We are happy."
@@ -234,7 +249,9 @@ class Solution {
```
### 解法
+
#### 解法一
+
利用正则匹配替换。
```java
@@ -253,6 +270,7 @@ class Solution {
```
#### 解法二
+
先遍历原字符串,遇到空格,则在原字符串末尾 `append` 任意两个字符,如两个空格。
用指针 `i` 指向原字符串末尾,`j` 指向现字符串末尾,`i`, `j` 从后往前遍历,当 `i` 遇到空格,`j` 位置依次要赋值为 `'0','2','%'`,若不是空格,直接赋值为 `i` 指向的字符。
@@ -274,7 +292,7 @@ class Solution {
if (str == null) {
return null;
}
-
+
int len = str.length();
for (int i = 0; i < len; ++i) {
if (str.charAt(i) == ' ') {
@@ -299,20 +317,24 @@ class Solution {
```
## 6 从尾到头打印链表
+
来源:[AcWing](https://www.acwing.com/problem/content/18/)
### 题目描述
+
输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。
返回的结果用数组存储。
**样例**
+
```
输入:[2, 3, 5]
返回:[5, 3, 2]
```
### 解法
+
遍历链表,每个链表结点值 `push` 进栈,最后将栈中元素依次 `pop` 到数组中。
```java
@@ -356,11 +378,15 @@ class Solution {
```
## 7 重建二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/23/)
+
### 题目描述
+
输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。
**样例**
+
```
给定:
前序遍历是:[3, 9, 20, 15, 7]
@@ -376,6 +402,7 @@ class Solution {
```
### 解法
+
在二叉树的前序遍历序列中,第一个数字总是根结点的值。在中序遍历序列中,根结点的值在序列的中间,左子树的结点位于根结点左侧,而右子树的结点位于根结点值的右侧。
遍历中序序列,找到根结点,递归构建左子树与右子树。
@@ -432,8 +459,11 @@ class Solution {
```
## 8 二叉树的下一个节点
+
来源:[AcWing](https://www.acwing.com/problem/content/31/)
+
### 题目描述
+
给定一棵二叉树的其中一个节点,请找出中序遍历序列的下一个节点。
**注意:**
@@ -442,6 +472,7 @@ class Solution {
- 二叉树一定不为空,且给定的节点一定不是空节点。
**样例**
+
```
假定二叉树是:[2, 1, 3, null, null, null, null], 给出的是值等于 2 的节点。
@@ -454,11 +485,13 @@ class Solution {
```
### 解法
+
对于结点 `p`:
+
- 如果它有右子树,则**右子树的最左结点**就是它的下一个结点;
- 如果它没有右子树,判断它与父结点 `p.father` 的位置情况:
- - 如果它是父结点的左孩子,那么父结点 `p.father` 就是它的下一个结点;
- - 如果它是父结点的右孩子,一直向上寻找,直到找到某个结点,它是它父结点的左孩子,那么该父结点就是 `p` 的下一个结点。
+ - 如果它是父结点的左孩子,那么父结点 `p.father` 就是它的下一个结点;
+ - 如果它是父结点的右孩子,一直向上寻找,直到找到某个结点,它是它父结点的左孩子,那么该父结点就是 `p` 的下一个结点。
```java
/**
@@ -506,11 +539,14 @@ class Solution {
```
## 9.1 用两个栈实现队列
+
来源:[AcWing](https://www.acwing.com/problem/content/36/)
+
### 题目描述
+
请用栈实现一个队列,支持如下四种操作:
-- push(x) – 将元素x插到队尾。
+- push(x) – 将元素 x 插到队尾。
- pop(x) – 将队首的元素弹出,并返回该元素。
- peek() – 返回队首元素。
- empty() – 返回队列是否为空。
@@ -522,6 +558,7 @@ class Solution {
- 输入数据保证合法,例如,在队列为空时,不会进行 `pop` 或者 `peek` 等操作;
**样例**
+
```java
MyQueue queue = new MyQueue();
@@ -533,8 +570,10 @@ queue.empty(); // returns false
```
### 解法
+
`push` 操作,每次都存入 `s1`;
`pop` 操作,每次从 `s2` 取:
+
- `s2` 栈不为空时,不能将 `s1` 元素倒入;
- `s2` 栈为空时,需要一次将 `s1` 元素全部倒入。
@@ -589,8 +628,11 @@ class MyQueue {
```
## 9.2 用两个队列实现栈
-来源:[LeetCode](https://leetcode-cn.com/problems/implement-stack-using-queues/)
+
+来源:[LeetCode](https://leetcode.cn/problems/implement-stack-using-queues/)
+
### 题目描述
+
使用队列实现栈的下列操作:
- push(x) -- 元素 x 入栈
@@ -604,8 +646,8 @@ class MyQueue {
- 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
- 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
-
### 解法
+
- 出栈时,先将队列的元素依次移入另一个队列中,直到队列剩下一个元素。将该元素出队即可。
- 进栈时,将元素压入不为空的那一个队列即可。如果两队列都为空,随便压入其中一个队列。
@@ -669,21 +711,27 @@ class MyStack {
```
## 10.1 斐波那契数列
+
来源:[AcWing](https://www.acwing.com/problem/content/19/)
+
### 题目描述
+
输入一个整数 n ,求斐波那契数列的第 n 项。
假定从 0 开始,第 0 项为 0。`(n<=39)`
**样例**
+
```
-输入整数 n=5
+输入整数 n=5
返回 5
```
### 解法
+
#### 解法一
+
采用递归方式,简洁明了,但效率很低,存在大量的重复计算。
```
@@ -692,7 +740,7 @@ class MyStack {
f(9) f(8)
/ \ / \
f(8) f(7) f(7) f(6)
- / \ / \
+ / \ / \
f(7) f(6) f(6) f(5)
```
@@ -715,6 +763,7 @@ class Solution {
```
#### 解法二
+
从下往上计算,递推,时间复杂度 `O(n)`。可以用数组存储,空间复杂度 `O(n)`;也可以用变量存储,空间复杂度 `O(1)`。
```java
@@ -742,12 +791,17 @@ class Solution {
```
## 10.2 跳台阶
+
来源:[NowCoder](https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4?tpId=13&tqId=11161&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
### 题目描述
+
一只青蛙一次可以跳上`1`级台阶,也可以跳上`2`级。求该青蛙跳上一个`n`级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
### 解法
+
跳上 `n` 级台阶,可以从 `n-1` 级跳 `1` 级上去,也可以从 `n-2` 级跳 `2` 级上去。所以
+
```
f(n) = f(n-1) + f(n-2)
```
@@ -776,18 +830,25 @@ class Solution {
```
## 10.3 变态跳台阶
+
来源:[NowCoder](https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
### 题目描述
+
一只青蛙一次可以跳上`1`级台阶,也可以跳上`2`级……它也可以跳上`n`级。求该青蛙跳上一个`n`级的台阶总共有多少种跳法。
### 解法
+
#### 解法一:数学推导
+
跳上 `n-1` 级台阶,可以从 `n-2` 级跳 `1` 级上去,也可以从 `n-3` 级跳 `2` 级上去...那么
+
```
f(n-1) = f(n-2) + f(n-3) + ... + f(0)
```
跳上 `n` 级台阶,可以从 `n-1` 级跳 `1` 级上去,也可以从 `n-2` 级跳 `2` 级上去...那么
+
```
f(n) = f(n-1) + f(n-2) + ... + f(0)
```
@@ -799,6 +860,7 @@ f(n) - f(n-1) = f(n-1)
```
即
+
```
f(n) = 2*f(n-1)
```
@@ -823,6 +885,7 @@ class Solution {
**注意**,这一解法已同步贡献给开源仓库 [CS-Notes](https://github.com/CyC2018/CS-Notes/pull/496)。
#### 解法二:动态规划
+
每当计算 res[i],把前面所有结果累加起来。
```java
@@ -851,12 +914,17 @@ class Solution {
```
## 10.4 矩形覆盖
+
来源:[NowCoder](https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6?tpId=13&tqId=11163&tPage=1&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking)
+
### 题目描述
+
我们可以用`2*1`的小矩形横着或者竖着去覆盖更大的矩形。请问用`n`个`2*1`的小矩形无重叠地覆盖一个`2*n`的大矩形,总共有多少种方法?
### 解法
+
覆盖 `2*n` 的矩形:
+
- 可以先覆盖 `2*n-1` 的矩形,再覆盖一个 `2*1` 的矩形;
- 也可以先覆盖 `2*(n-2)` 的矩形,再覆盖两个 `1*2` 的矩形。
@@ -867,7 +935,7 @@ class Solution {
/**
* 矩形覆盖
- *
+ *
* @param target 2*target大小的矩形
* @return 多少种覆盖方法
*/
@@ -912,8 +980,11 @@ class Solution {
```
## 11 旋转数组的最小数字
+
来源:[AcWing](https://www.acwing.com/problem/content/20/)
+
### 题目描述
+
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个升序的数组的一个旋转,输出旋转数组的最小元素。
@@ -925,6 +996,7 @@ class Solution {
**注意**:数组内所含元素非负,若数组大小为 0,请返回 -1。
**样例**
+
```
输入:nums=[2,2,2,0,1]
@@ -932,7 +1004,9 @@ class Solution {
```
### 解法
+
#### 解法一
+
直接遍历数组找最小值,时间复杂度 `O(n)`,不推荐。
```java
@@ -962,9 +1036,11 @@ class Solution {
```
#### 解法二
+
利用指针 `start`,`end` 指向数组的首尾,如果 `nums[start] < nums[end]`,说明数组是递增数组,直接返回 `nums[start]`。否则进行如下讨论。
计算中间指针 `mid`:
+
- 如果此时 `nums[start]`, `nums[end]`, `nums[mid]` 两两相等,此时无法采用二分方式,只能通过遍历区间 `[start,end)` 获取最小值;
- 如果此时 `start`,`end` 相邻,说明此时 `end` 指向的元素是最小值,返回 `nums[end]`;
- 如果此时 `nums[mid] >= nums[start]`,说明 `mid` 位于左边的递增数组中,最小值在右边,因此,把 `start` 指向 `mid`,此时保持了 `start` 指向左边递增子数组;
@@ -1020,8 +1096,11 @@ class Solution {
```
## 12 矩阵中的路径
+
来源:[AcWing](https://www.acwing.com/problem/content/21/)
+
### 题目描述
+
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
@@ -1034,6 +1113,7 @@ class Solution {
- 所有出现的字符均为大写英文字母。
**样例**
+
```
matrix=
[
@@ -1042,12 +1122,13 @@ matrix=
['A','D','E','E']
]
-str="BCCE" , return "true"
+str="BCCE" , return "true"
str="ASAE" , return "false"
```
### 解法
+
回溯法。首先,任选一个格子作为路径起点。假设格子对应的字符为 ch,并且对应路径上的第 i 个字符。若相等,到相邻格子寻找路径上的第 i+1 个字符。重复这一过程。
```java
@@ -1103,8 +1184,11 @@ class Solution {
```
## 13 机器人的运动范围
+
来源:[AcWing](https://www.acwing.com/problem/content/22/)
+
### 题目描述
+
地上有一个 m 行和 n 列的方格。
一个机器人从坐标 `0,0` 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。
@@ -1113,14 +1197,16 @@ class Solution {
请问该机器人能够达到多少个格子?
-**样例1**
+**样例 1**
+
```
输入:k=7, m=4, n=5
输出:20
```
-**样例2**
+**样例 2**
+
```
输入:k=18, m=40, n=40
@@ -1137,6 +1223,7 @@ class Solution {
3. 0<=k<=100
### 解法
+
从坐标(0, 0) 开始移动,当它准备进入坐标(i, j),判断是否能进入,如果能,再判断它能否进入 4 个相邻的格子 (i-1, j), (i+1, j), (i, j-1), (i, j+1)。
```java
@@ -1183,8 +1270,11 @@ class Solution {
```
## 14 剪绳子
+
来源:[AcWing](https://www.acwing.com/problem/content/24/)
+
### 题目描述
+
给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,n>1 并且 m≥1)。
每段的绳子的长度记为 `k[0]、k[1]、……、k[m]`。`k[0]k[1] … k[m]`可能的最大乘积是多少?
@@ -1192,6 +1282,7 @@ class Solution {
例如当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到最大的乘积 18。
**样例**
+
```
输入:8
@@ -1199,7 +1290,9 @@ class Solution {
```
### 解法
+
#### 解法一:动态规划法
+
时间复杂度`O(n²)`,空间复杂度`O(n)`。
```
@@ -1239,13 +1332,16 @@ class Solution {
```
#### 贪心算法
+
时间复杂度`O(1)`,空间复杂度`O(1)`。
贪心策略:
+
- 当 n>=5 时,尽可能多地剪长度为 3 的绳子
- 当剩下的绳子长度为 4 时,就把绳子剪成两段长度为 2 的绳子。
**证明:**
+
- 当 n>=5 时,可以证明 2(n-2)>n,并且 3(n-3)>n。也就是说,当绳子剩下长度大于或者等于 5 的时候,可以把它剪成长度为 3 或者 2 的绳子段。
- 当 n>=5 时,3(n-3)>=2(n-2),因此,应该尽可能多地剪长度为 3 的绳子段。
- 当 n=4 时,剪成两根长度为 2 的绳子,其实没必要剪,只是题目的要求是至少要剪一刀。
@@ -1274,23 +1370,28 @@ class Solution {
}
```
-## 15 二进制中1的个数
+## 15 二进制中 1 的个数
+
来源:[AcWing](https://www.acwing.com/problem/content/25/)
+
### 题目描述
+
输入一个 32 位整数,输出该数二进制表示中 1 的个数。
**注意**:
- 负数在计算机中用其绝对值的补码来表示。
-**样例1**
+**样例 1**
+
```
输入:9
输出:2
解释:9的二进制表示是1001,一共有2个1。
```
-**样例2**
+**样例 2**
+
```
输入:-2
输出:31
@@ -1299,8 +1400,10 @@ class Solution {
```
### 解法
+
#### 解法一
-利用整数 1,依次左移每次与 n 进行与运算,若结果不为0,说明这一位上数字为 1,++cnt。
+
+利用整数 1,依次左移每次与 n 进行与运算,若结果不为 0,说明这一位上数字为 1,++cnt。
此解法 i 需要左移 32 次。
@@ -1311,7 +1414,7 @@ class Solution {
/**
* 求二进制中1的个数
- *
+ *
* @param n 整数
* @return 该整数的二进制中1的个数
*/
@@ -1330,11 +1433,13 @@ class Solution {
```
#### 解法二(推荐)
+
运算 `(n - 1) & n`,直至 n 为 0。运算的次数即为 n 的二进制中 1 的个数。
因为 n-1 会将 n 的最右边一位 1 改为 0,如果右边还有 0,则所有 0 都会变成 1。结果与 n 进行与运算,会去除掉最右边的一个 1。
举个栗子:
+
```
若 n = 1100,
n - 1 = 1011
@@ -1350,7 +1455,7 @@ class Solution {
/**
* 求二进制中1的个数
- *
+ *
* @param n 整数
* @return 该整数的二进制中1的个数
*/
@@ -1366,7 +1471,9 @@ class Solution {
```
#### 解法三
+
利用 Java API。
+
```java
class Solution {
@@ -1383,9 +1490,12 @@ class Solution {
```
## 16 数值的整数次方
+
来源:[AcWing](https://www.acwing.com/problem/content/26/)
+
### 题目描述
-实现函数 double Power(double base, int exponent),求 base 的 exponent 次方。
+
+实现函数 double Power(double base, int exponent),求 base 的 exponent 次方。
不得使用库函数,同时不需要考虑大数问题。
@@ -1393,24 +1503,28 @@ class Solution {
- 不会出现底数和指数同为 0 的情况。
-**样例1**
+**样例 1**
+
```
输入:10 ,2
输出:100
```
-**样例2**
+**样例 2**
+
```
-输入:10 ,-2
+输入:10 ,-2
输出:0.01
```
### 解法
+
注意判断值数是否小于 0。另外 0 的 0 次方没有意义,也需要考虑一下,看具体题目要求。
#### 解法一
+
时间复杂度 `O(N)`。
```java
@@ -1442,7 +1556,8 @@ class Solution {
```
#### 解法二
-
+
+
递归求解,每次 exponent 缩小一半,时间复杂度为 `O(log N)`。
@@ -1474,25 +1589,29 @@ class Solution {
}
```
-## 17 打印从1到最大的n位数
+## 17 打印从 1 到最大的 n 位数
+
来源:无
+
### 题目描述
+
输入数字 `n`,按顺序打印出从 `1` 最大的 `n` 位十进制数。比如输入 `3`,则打印出 `1、2、3` 一直到最大的 3 位数即 999。
### 解法
+
此题需要注意 n 位数构成的数字可能超出最大的 int 或者 long long 能表示的范围。因此,采用字符数组来存储数字。
#### 解法一
- 对字符数组表示的数进行递增操作;
-- 输出数字(0开头的需要把0去除)。
+- 输出数字(0 开头的需要把 0 去除)。
```java
class Solution {
/**
* 打印从1到最大的n位数
- *
+ *
* @param n n位数
*/
public void print1ToMaxOfNDigits(int n) {
@@ -1509,7 +1628,7 @@ class Solution {
/**
* 打印字符数组表示的数字(需要省略前n个0)
- *
+ *
* @param chars 字符数组
*/
private void printNumber(char[] chars) {
@@ -1601,14 +1720,18 @@ class Solution {
}
```
-## 18.1 在O(1)时间删除链表节点
+## 18.1 在 O(1)时间删除链表节点
+
来源:[AcWing](https://www.acwing.com/problem/content/85/)
+
### 题目描述
+
给定单向链表的一个节点指针,定义一个函数在 `O(1)` 时间删除该节点。
假设链表一定存在,并且该节点一定不是尾节点。
**样例**
+
```
输入:链表 1->4->6->8
删掉节点:第2个节点即6(头节点为第0个节点)
@@ -1617,6 +1740,7 @@ class Solution {
```
### 解法
+
判断要删除的节点是否是尾节点:
- 若是,那么需要遍历链表,找到节点的前一个节点,让前一个节点指向 `null`,时间复杂度为 `O(n)`;
@@ -1637,7 +1761,7 @@ class Solution {
/**
* 删除链表的节点
- *
+ *
* @param node 要删除的节点
*/
public void deleteNode(ListNode node) {
@@ -1648,18 +1772,23 @@ class Solution {
```
## 18.2 删除链表中重复的节点
+
来源:[AcWing](https://www.acwing.com/problem/content/27/)
+
### 题目描述
+
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留。
-**样例1**
+**样例 1**
+
```
输入:1->2->3->3->4->4->5
输出:1->2->5
```
-**样例2**
+**样例 2**
+
```
输入:1->1->1->2->3
@@ -1667,6 +1796,7 @@ class Solution {
```
### 解法
+
#### 解法一:递归
```java
@@ -1707,6 +1837,7 @@ class Solution {
```
#### 解法二:非递归
+
pre 始终指向下一个不重复的节点。
```java
@@ -1754,8 +1885,11 @@ class Solution {
```
## 19 正则表达式匹配
+
来源:[AcWing](https://www.acwing.com/problem/content/28/)
+
### 题目描述
+
请实现一个函数用来匹配包括 `'.'` 和 `'*'` 的正则表达式。
模式中的字符 `'.'` 表示任意一个字符,而 `'*'` 表示它前面的字符可以出现任意次(含 0 次)。
@@ -1765,6 +1899,7 @@ class Solution {
例如,字符串 `"aaa"` 与模式 `"a.a"` 和 `"ab*ac*a"` 匹配,但是与 `"aa.a"` 和 `"ab*a"` 均不匹配。
**样例**
+
```
输入:
@@ -1775,14 +1910,15 @@ p="a*"
```
### 解法
+
判断模式中第二个字符是否是 `*`:
- 若是,看如果模式串第一个字符与字符串第一个字符是否匹配:
- - 若不匹配,在模式串上向右移动两个字符`j+2`,相当于 a* 被忽略。
- - 若匹配,字符串后移`i+1`。此时模式串可以移动两个字符`j+2`,也可以不移动`j`。
+ - 若不匹配,在模式串上向右移动两个字符`j+2`,相当于 a\* 被忽略。
+ - 若匹配,字符串后移`i+1`。此时模式串可以移动两个字符`j+2`,也可以不移动`j`。
- 若不是,看当前字符与模式串的当前字符是否匹配,即 `str[i] == pattern[j] || pattern[j] == '.'`:
- - 若匹配,则字符串与模式串都向右移动一位,`i+1`,`j+1`。
- - 若不匹配,返回 false。
+ - 若匹配,则字符串与模式串都向右移动一位,`i+1`,`j+1`。
+ - 若不匹配,返回 false。
```java
class Solution {
@@ -1833,8 +1969,11 @@ class Solution {
```
## 20 表示数值的字符串
+
来源:[AcWing](https://www.acwing.com/problem/content/29/)
+
### 题目描述
+
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
例如,字符串`"+100"`,`"5e2"`,`"-123"`,`"3.1416"`和`"-1E-16"`都表示数值。
@@ -1843,13 +1982,14 @@ class Solution {
**注意**:
-- 小数可以没有整数部分,例如.123等于0.123;
-- 小数点后面可以没有数字,例如233.等于233.0;
-- 小数点前面和后面可以有数字,例如233.666;
-- 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
-- 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4;
+- 小数可以没有整数部分,例如.123 等于 0.123;
+- 小数点后面可以没有数字,例如 233.等于 233.0;
+- 小数点前面和后面可以有数字,例如 233.666;
+- 当 e 或 E 前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
+- 当 e 或 E 后面没有整数时,整个字符串不能表示数字,例如 12e、12e+5.4;
**样例**:
+
```
输入: "0"
@@ -1857,7 +1997,9 @@ class Solution {
```
### 解法
+
利用正则表达式匹配即可。
+
```
[] : 字符集合
() : 分组
@@ -1877,20 +2019,25 @@ public class Solution {
* @return
*/
public boolean isNumeric(char[] str) {
- return str != null
- && str.length != 0
+ return str != null
+ && str.length != 0
&& new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
}
}
```
## 21 调整数组顺序使奇数位于偶数前面
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
### 解法
+
#### 解法一
+
计算出奇数的个数,就很容易写出来了。
```java
@@ -1927,6 +2074,7 @@ public class Solution {
```
#### 解法二
+
```java
import java.util.Arrays;
@@ -1940,16 +2088,20 @@ public class Solution {
Arrays.sort(bak, (x, y) -> (y & 1) - (x & 1));
Arrays.setAll(array, i -> bak[i]);
}
-
+
}
```
-## 22 链表中倒数第k个节点
+## 22 链表中倒数第 k 个节点
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
-输入一个链表,输出该链表中倒数第k个结点。
+
+输入一个链表,输出该链表中倒数第 k 个结点。
### 解法
+
pre 指针走 `k-1` 步。之后 cur 指针指向 phead,然后两个指针同时走,直至 pre 指针到达尾结点。
> 当用一个指针遍历链表不能解决问题的时候,可以尝试用两个指针来遍历链表。可以让其中一个指针遍历的速度快一些。
@@ -1998,11 +2150,15 @@ public class Solution {
```
## 23 链表中环的入口结点
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出`null`。
### 解法
+
- 先利用快慢指针。若能相遇,说明存在环,且相遇点一定是在环上;若没有相遇,说明不存在环,返回 `null`。
- 固定当前相遇点,用一个指针继续走,同时累积结点数。计算出环的结点个数 `cnt`。
- 指针 p1 先走 `cnt` 步,p2 指向链表头部,之后 `p1`,`p2` 同时走,相遇时,相遇点一定是在环的入口处。因为 `p1` 比 `p2` 多走了环的一圈。
@@ -2072,12 +2228,17 @@ public class Solution {
```
## 24 反转链表
+
来源:[AcWing](https://www.acwing.com/problem/content/33/)
+
### 题目描述
+
输入一个链表,反转链表后,输出新链表的表头。
### 解法
+
#### 解法一
+
利用头插法解决。
```java
@@ -2138,11 +2299,15 @@ class Solution {
```
## 25 合并两个排序的链表
+
来源:[AcWing](https://www.acwing.com/problem/content/34/)
+
### 题目描述
+
输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
**样例**
+
```
输入:1->3->5 , 2->4->5
@@ -2150,7 +2315,9 @@ class Solution {
```
### 解法
+
#### 解法一
+
同时遍历两链表进行 `merge`。
```java
@@ -2195,6 +2362,7 @@ class Solution {
```
#### 解法二:递归
+
```java
/**
* Definition for singly-linked list.
@@ -2223,8 +2391,11 @@ class Solution {
```
## 26 树的子结构
+
来源:[AcWing](https://www.acwing.com/problem/content/35/)
+
### 题目描述
+
输入两棵二叉树 A、B,判断 B 是不是 A 的子结构。
我们规定空树不是任何树的子结构。
@@ -2232,6 +2403,7 @@ class Solution {
**样例**
树 A:
+
```
8
/ \
@@ -2243,6 +2415,7 @@ class Solution {
```
树 B:
+
```
8
/ \
@@ -2252,6 +2425,7 @@ class Solution {
返回 true ,因为 B 是 A 的子结构。
### 解法
+
递归方式遍历:
- 在树 A 中找到和树 B 的根结点值一样的结点 R;
@@ -2282,9 +2456,9 @@ class Solution {
}
}
return res;
-
+
}
-
+
private boolean isSame(TreeNode root1, TreeNode root2) {
if (root2 == null) {
return true;
@@ -2297,13 +2471,16 @@ class Solution {
}
```
-
## 27 二叉树的镜像
+
来源:[AcWing](https://www.acwing.com/problem/content/37/)
+
### 题目描述
+
输入一个二叉树,将它变换为它的镜像。
**样例**
+
```
输入树:
8
@@ -2312,7 +2489,7 @@ class Solution {
/ \ / \
5 7 9 11
- [8,6,10,5,7,9,11,null,null,null,null,null,null,null,null]
+ [8,6,10,5,7,9,11,null,null,null,null,null,null,null,null]
输出树:
8
/ \
@@ -2324,6 +2501,7 @@ class Solution {
```
### 解法
+
将根结点的左右孩子互换,之后递归左右孩子。
```java
@@ -2351,13 +2529,17 @@ class Solution {
```
## 28 对称的二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/38/)
+
### 题目描述
+
请实现一个函数,用来判断一棵二叉树是不是对称的。
如果一棵二叉树和它的镜像一样,那么它是对称的。
**样例**
+
```
如下图所示二叉树[1,2,2,3,4,4,3,null,null,null,null,null,null,null,null]为对称二叉树:
1
@@ -2375,6 +2557,7 @@ class Solution {
```
### 解法
+
比较二叉树的前序遍历序列和对称前序遍历序列是否一样,若是,说明是对称的。
```java
@@ -2391,7 +2574,7 @@ class Solution {
public boolean isSymmetric(TreeNode root) {
return isSymmetric(root, root);
}
-
+
private boolean isSymmetric(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null) {
return true;
@@ -2404,13 +2587,16 @@ class Solution {
}
```
-
## 29 顺时针打印矩阵
+
来源:[AcWing](https://www.acwing.com/problem/content/39/)
+
### 题目描述
+
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
**样例**
+
```
输入:
[
@@ -2423,6 +2609,7 @@ class Solution {
```
### 解法
+
由外往里,一圈圈打印矩阵即可。
```java
@@ -2441,7 +2628,7 @@ class Solution {
}
return res;
}
-
+
private void add(int[][] matrix, int[] res, int[] index, int i, int j, int p, int q) {
if (i == p) {
for (int m = j; m <= q; ++m) {
@@ -2465,23 +2652,26 @@ class Solution {
res[index[0]++] = matrix[m][j];
}
}
-
+
}
}
```
+## 30 包含 min 函数的栈
-## 30 包含min函数的栈
来源:[AcWing](https://www.acwing.com/problem/content/90/)
+
### 题目描述
+
设计一个支持 push,pop,top 等操作并且可以在 O(1) 时间内检索出最小元素的堆栈。
-- push(x)–将元素x插入栈中
+- push(x)–将元素 x 插入栈中
- pop()–移除栈顶元素
- top()–得到栈顶元素
- getMin()–得到栈中最小元素
**样例**
+
```
MinStack minStack = new MinStack();
minStack.push(-1);
@@ -2494,6 +2684,7 @@ minStack.getMin(); --> Returns -1.
```
### 解法
+
定义两个`stack`。
压栈时,先将元素 `x` 压入 `stack1`。然后判断 `stack2` 的情况:
@@ -2514,7 +2705,7 @@ class MinStack {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
-
+
public void push(int x) {
stack1.push(x);
if (stack2.isEmpty() || stack2.peek() > x) {
@@ -2523,16 +2714,16 @@ class MinStack {
stack2.push(stack2.peek());
}
}
-
+
public void pop() {
stack1.pop();
stack2.pop();
}
-
+
public int top() {
return stack1.peek();
}
-
+
public int getMin() {
return stack2.peek();
}
@@ -2549,8 +2740,11 @@ class MinStack {
```
## 31 栈的压入、弹出序列
+
来源:[AcWing](https://www.acwing.com/problem/content/40/)
+
### 题目描述
+
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
假设压入栈的所有数字均不相等。
@@ -2560,6 +2754,7 @@ class MinStack {
注意:若两个序列为空或长度不等则视为并不是一个栈的压入、弹出序列。
**样例**
+
```
输入:[1,2,3,4,5]
[4,5,3,2,1]
@@ -2568,6 +2763,7 @@ class MinStack {
```
### 解法
+
判断下一个要弹出的元素:
- 如果刚好是栈顶元素,直接弹出。
@@ -2612,13 +2808,16 @@ public class Solution {
}
```
-
## 32.1 不分行从上往下打印二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
### 解法
+
先将根节点进入队列。
队头元素出队,将值存入 list,判断该元素是否有左/右子树,有的话依次进入队列中。队列为空时结束。
@@ -2670,13 +2869,16 @@ public class Solution {
}
```
-
## 32.2 分行从上往下打印二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
### 解法
+
与上一题类似,只不过需要用变量记录每一层要打印多少个节点。
```java
@@ -2737,13 +2939,16 @@ public class Solution {
}
```
-
## 32.3 之字形打印二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
如二叉树:
+
```
1
/ \
@@ -2753,6 +2958,7 @@ public class Solution {
```
打印结果为:
+
```
1
3 2
@@ -2760,14 +2966,14 @@ public class Solution {
```
### 解法
-对于上述二叉树:
-首先访问根结点,之后把2、3存入某结构。打印的时候,先打印3、2。这不就是栈?
+对于上述二叉树:
-依次弹出栈元素,分别是3、2。弹出时需要把3、2的子结点存入结构。由于访问时顺序是`4 5 6 7`。所以也需要用栈来存放。而且,此时需要先存放右孩子,再存放左孩子。(奇数层/偶数层存放左右孩子的顺序不同)
+首先访问根结点,之后把 2、3 存入某结构。打印的时候,先打印 3、2。这不就是栈?
-这里需要用两个栈来实现。如果只用一个栈,那么当弹出3、2 时,先将 3 的孩子节点压入栈。之后弹栈的时候不是先弹出 2,而是弹出了 3 的 孩子节点,就错了。
+依次弹出栈元素,分别是 3、2。弹出时需要把 3、2 的子结点存入结构。由于访问时顺序是`4 5 6 7`。所以也需要用栈来存放。而且,此时需要先存放右孩子,再存放左孩子。(奇数层/偶数层存放左右孩子的顺序不同)
+这里需要用两个栈来实现。如果只用一个栈,那么当弹出 3、2 时,先将 3 的孩子节点压入栈。之后弹栈的时候不是先弹出 2,而是弹出了 3 的 孩子节点,就错了。
```java
import java.util.ArrayList;
@@ -2835,20 +3041,23 @@ public class Solution {
}
```
-
## 33 二叉搜索树的后序遍历序列
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出`Yes`,否则输出`No`。假设输入的数组的任意两个数字都互不相同。
### 解法
+
序列的最后一个元素是二叉搜索树的根节点。
在序列中从左到右找到根节点的左子树(比根节点小)、右子树(比根节点大)。
+
- 如果右子树中出现比根节点小的元素,那么为 false。
- 否则递归左右子树。
-
```java
public class Solution {
/**
@@ -2888,10 +3097,12 @@ public class Solution {
}
```
-
## 34 二叉树中和为某一值的路径
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的`list`中,数组长度大的数组靠前)
### 解法
@@ -2914,12 +3125,12 @@ import java.util.ArrayList;
}
*/
public class Solution {
-
+
private ArrayList> res = new ArrayList<>();
/**
* 找出二叉树中和为某一值的路径(必须从根节点到叶节点)
- *
+ *
* @param root 二叉树的根结点
* @param target 目标值
* @return 结果list
@@ -2946,23 +3157,26 @@ public class Solution {
}
```
-
## 35 复杂链表的复刻
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 `head`。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
-
+
### 解法
+
- 第一步,在每个节点的后面插入复制的节点;
-
+ 
- 第二步,对复制节点的 random 链接进行赋值;
-
+ 
- 第三步,分离两个链表。
-
+ 
```java
/*
@@ -3016,14 +3230,19 @@ public class Solution {
```
## 36 二叉搜索树与双向链表
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
### 解法
+
由于是二叉搜索树,因此中序遍历的结果就是排序的。
中序遍历利用栈来实现。遍历时,前一个结点的 right 指向后一个结点,后一个结点的 left 指向前一个结点。
+
```java
pre.right = cur
cur.left = pre
@@ -3048,7 +3267,7 @@ import java.util.Stack;
public class Solution {
/**
* 将二叉搜索树转换为双向链表
- *
+ *
* @param pRootOfTree
* @return
*/
@@ -3083,14 +3302,18 @@ public class Solution {
}
```
-
## 39 数组中出现次数超过一半的数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为 9 的数组 `{1,2,3,2,2,2,5,4,2}`。由于数字 2 在数组中出现了 5 次,超过数组长度的一半,因此输出 2。如果不存在则输出 0。
### 解法
+
#### 解法一
+
利用快排中的 partition 思想。
数组中有一个数字出现次数超过了数组长度的一半,那么排序后,数组中间的数字一定就是我们要找的数字。我们随机选一个数字,利用 partition() 函数,使得比选中数字小的数字都排在它左边,比选中数字大的数字都排在它的右边。
@@ -3173,13 +3396,14 @@ public class Solution {
++cnt;
}
}
-
+
return cnt * 2 > array.length;
}
}
```
#### 解法二
+
利用多数投票算法,从头到尾遍历数组,遇到两个不一样的数就把这两个数同时除去。除去的两个数可能都不是 majority,也可能一个是 majority 另一个不是,但是因为 majority 总数大于一半,所以这么删完最后剩下的肯定是 majority。
此方法时间复杂度为 `O(n)`,且不会改变数组。
@@ -3196,7 +3420,7 @@ public class Solution {
if (array == null || array.length == 0) {
return 0;
}
-
+
int res = array[0];
int times = 1;
for (int i = 1; i < array.length; ++i) {
@@ -3234,13 +3458,18 @@ public class Solution {
}
```
-## 40 最小的k个数
+## 40 最小的 k 个数
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入 n 个整数,找出其中最小的 K 个数。例如输入 `4,5,1,6,2,7,3,8` 这 8 个数字,则最小的 4 个数字是 `1,2,3,4`。
### 解法
+
#### 解法一
+
利用快排中的 partition 思想。
数组中有一个数字出现次数超过了数组长度的一半,那么排序后,数组中间的数字一定就是我们要找的数字。我们随机选一个数字,利用 partition() 函数,使得比选中数字小的数字都排在它左边,比选中数字大的数字都排在它的右边。
@@ -3308,6 +3537,7 @@ public class Solution {
```
#### 解法二
+
利用大根堆,存储最小的 k 个数,最后返回即可。
此方法时间复杂度为 `O(nlogk)`。虽然慢一点,但是它不会改变输入的数组,并且它**适合海量数据的输入**。
@@ -3354,15 +3584,17 @@ public class Solution {
}
```
-
## 41 数据流中的中位数
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用`Insert()`方法读取数据流,使用`GetMedian()`方法获取当前读取数据的中位数。
### 解法
-利用大根堆存放较小的一半元素,小根堆存放较大的一半元素。维持大小堆的元素个数差不超过 1。
+利用大根堆存放较小的一半元素,小根堆存放较大的一半元素。维持大小堆的元素个数差不超过 1。
```java
import java.util.Comparator;
@@ -3414,16 +3646,19 @@ public class Solution {
}
```
-
## 42 连续子数组的最大和
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个**非空**整型数组,数组里的数可能为正,也可能为负。
数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为`O(n)`。
### 解法
+
动态规划法。
res[i] 表示以第 i 个数字结尾的子数组的最大和,那么求出 `max(res[i])` 即可。
@@ -3454,8 +3689,11 @@ public class Solution {
```
## 44 数字序列中某一位的数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
数字以 `0123456789101112131415…` 的格式序列化到一个字符序列中。
在这个序列中,第 5 位(从 0 开始计数)是 5,第 13 位是 1,第 19 位是 4,等等。
@@ -3463,6 +3701,7 @@ public class Solution {
请写一个函数求任意位对应的数字。
### 解法
+
举个栗子,求序列第 1001 位。
序列的前 10 位是 `0~9`, 这 10 个只有一位的数字。显然第 1001 位在这 10 个数字之后,因此这 10 个数字可以直接跳过。再从后面序列中找第 991(991=1001-10) 位的数字。接下来有 90 个两位数,共 180 位,由于 991>180,所以继续跳过。从 881 找...最后可以找到对应的数字以及数字的某一位。
@@ -3516,17 +3755,18 @@ public class Solution {
}
```
-
## 45 把数组排成最小的数
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
-例如输入数组 `[3, 32, 321]`,则打印出这3个数字能排成的最小数字`321323`。
+例如输入数组 `[3, 32, 321]`,则打印出这 3 个数字能排成的最小数字`321323`。
### 解法
-
```java
import java.util.Arrays;
@@ -3560,8 +3800,11 @@ class Solution {
```
## 46 把数字翻译成字符串
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
给定一个数字,我们按照如下规则把它翻译为字符串:
0 翻译成 ”a”,1 翻译成 ”b”,……,11 翻译成 ”l”,……,25 翻译成 ”z”。
@@ -3571,6 +3814,7 @@ class Solution {
请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。
### 解法
+
先写入递推式,res 表示共有多少种翻译方法。看最后一个字符,判断它与前一个字符能否构成有效翻译,计算 res[i]:
- 能,那么 `res[i] = res[i - 1] + res[i - 2]`;
@@ -3607,8 +3851,11 @@ class Solution {
```
## 47 礼物的最大价值
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
在一个 `m×n` 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。
你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或者向下移动一格直到到达棋盘的右下角。
@@ -3616,13 +3863,13 @@ class Solution {
给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?
### 解法
+
写出递推式,res 表示获得的最大礼物。
```java
res[i][j] = Math.max(res[i - 1][j], res[i][j - 1]) + grid[i][j];
```
-
```java
class Solution {
/**
@@ -3656,20 +3903,25 @@ class Solution {
```
## 48 长不含重复字符的子字符串
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
假设字符串中只包含从 `a` 到 `z`的字符。
### 解法
+
动态规划。
`res[i]` 表示以 `s[i]` 字符结尾的最长不重复字符串的长度。判断 `s[i]`:
+
- 若 `s[i]` 在前面没出现过,那么 `res[i] = res[i - 1] + 1`;
- 若 `s[i]` 在前面有出现过,判断它上一次出现的位置 `index` 到 `i` 的距离 `d` 与 `res[i - 1]` 的大小关系:
- - 若 `d <= res[i - 1]`,说明它被包含在 `res[i - 1]` 构成的子串中,那么 `res[i] = d`;
- - 若 `d > res[i - 1]`,说明它在 `res[i - 1]` 构成的子串的左侧,那么 `res[i] = res[i - 1] + 1`。
+ - 若 `d <= res[i - 1]`,说明它被包含在 `res[i - 1]` 构成的子串中,那么 `res[i] = d`;
+ - 若 `d > res[i - 1]`,说明它在 `res[i - 1]` 构成的子串的左侧,那么 `res[i] = res[i - 1] + 1`。
需要用一个数组 t 记录一下当前出现的字符在哪个位置。
@@ -3711,28 +3963,32 @@ class Solution {
```
## 52 两个链表的第一个公共结点
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入两个链表,找出它们的第一个公共结点。
**样例**
+
```
给出两个链表如下所示:
A: a1 → a2
↘
c1 → c2 → c3
- ↗
+ ↗
B: b1 → b2 → b3
输出第一个公共节点c1
```
### 解法
+
先遍历两链表,求出两链表的长度,再求长度差 `|n1 - n2|`。
较长的链表先走 `|n1 - n2|` 步,之后两链表再同时走,首次相遇时的节点即为两链表的第一个公共节点。
-
```java
/**
* Definition for singly-linked list.
@@ -3789,8 +4045,11 @@ class Solution {
```
## 53.1 数字在排序数组中出现的次数
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
统计一个数字在排序数组中出现的次数。
例如输入排序数组 `[1, 2, 3, 3, 3, 3, 4, 5]` 和数字 3,由于 3 在这个数组中出现了 4 次,因此输出 4。
@@ -3804,13 +4063,13 @@ class Solution {
```
### 解法
+
找出第一个 k 和最后一个 k 出现的位置。
找第一个 k 时,利用二分法,如果 `nums[m] == k`,判断它的前一个位置是不是也是 k,如果不是,说明这是第一个 k,直接返回。如果是,那么递归在左边查找第一个 k。
找最后一个 k 也同理。
-
```java
class Solution {
/**
@@ -3878,15 +4137,18 @@ class Solution {
}
```
+## 53.2 0 到 n-1 中缺失的数字
-## 53.2 0到n-1中缺失的数字
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
一个长度为 `n-1` 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 `0` 到 `n-1` 之内。
在范围 `0` 到 `n-1` 的 `n` 个数字中有且只有一个数字不在该数组中,请找出这个数字。
**样例**
+
```
输入:[0,1,2,4]
@@ -3894,13 +4156,14 @@ class Solution {
```
### 解法
+
找出第一个与下标不对应的数字即可。
特殊情况:
+
- 下标都对应,那么应该返回 `最后一个数+1`;
- 缺失的数字是第一个,那么返回 0。
-
```java
class Solution {
/**
@@ -3932,10 +4195,12 @@ class Solution {
}
```
-
## 53.3 数组中数值和下标相等的元素
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
假设一个单调递增的数组里的每个元素都是整数并且是唯一的。
请编程实现一个函数找出数组中任意一个数值等于其下标的元素。
@@ -3943,6 +4208,7 @@ class Solution {
例如,在数组 `[-3, -1, 1, 3, 5]` 中,数字 3 和它的下标相等。
**样例**
+
```
输入:[-3, -1, 1, 3, 5]
@@ -3952,12 +4218,13 @@ class Solution {
**注意**:如果不存在,则返回 -1。
### 解法
+
二分法查找。
+
- 当前元素等于对应的下标,直接返回该下标;
- 当前元素大于该下标,在左边查找;
- 当前元素小于该下标,在右边查找。
-
```java
class Solution {
/**
@@ -3988,13 +4255,17 @@ class Solution {
```
## 55.1 二叉树的深度
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一棵二叉树的根结点,求该树的深度。
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
**样例**
+
```
输入:二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示:
8
@@ -4007,8 +4278,8 @@ class Solution {
```
### 解法
-递归即可。
+递归即可。
```java
/**
@@ -4023,7 +4294,7 @@ class Solution {
class Solution {
/**
* 求二叉树的深度
- *
+ *
* @param root 二叉树根结点
* @return 深度
*/
@@ -4039,21 +4310,26 @@ class Solution {
```
### 测试用例
+
1. 功能测试(输入普通的二叉树;二叉树中所有节点都没有左/右子树);
2. 特殊输入测试(二叉树只有一个节点;二叉树的头节点为空指针)。
## 55.2 平衡二叉树
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一棵二叉树的根结点,判断该树是不是平衡二叉树。
-如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
+如果某二叉树中任意结点的左右子树的深度相差不超过 1,那么它就是一棵平衡二叉树。
**注意:**
- 规定空树也是一棵平衡二叉树。
**样例**
+
```
输入:二叉树[5,7,11,null,null,12,9,null,null,null,null]如下所示,
5
@@ -4066,12 +4342,13 @@ class Solution {
```
### 解法
+
#### 解法一
+
求每个节点左右孩子的深度,判断该节点是否平衡。
这种方法需要重复遍历节点多次,不推荐。
-
```java
/**
* Definition for a binary tree node.
@@ -4085,7 +4362,7 @@ class Solution {
class Solution {
/**
* 判断是否是平衡二叉树
- *
+ *
* @param root 二叉树根结点
* @return 是否是平衡二叉树
*/
@@ -4156,8 +4433,11 @@ class Solution {
```
## 56.1 数组中只出现一次的两个数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
一个整型数组里除了两个数字之外,其他的数字都出现了两次。
请写程序找出这两个只出现一次的数字。
@@ -4165,6 +4445,7 @@ class Solution {
你可以假设这两个数字一定存在。
**样例**
+
```
输入:[1,2,3,3,4,4]
@@ -4172,6 +4453,7 @@ class Solution {
```
### 解法
+
如果数组有一个数字出现一次,其它数字都出现两次。那么我们很容易通过异或 `^` 运算求出来。
而现在是有两个数字出现一次,那么我们考虑一下怎么将这两个数字隔开,之后我们对隔开的数组分别进行异或,不就求出来了?
@@ -4182,7 +4464,7 @@ class Solution {
class Solution {
/**
* 求数组中只出现一次的两个数字
- *
+ *
* @param nums 数字
* @return 两个数字组成的数组
*/
@@ -4227,8 +4509,11 @@ class Solution {
```
## 56.2 数组中唯一只出现一次的数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。
请找出那个只出现一次的数字。
@@ -4240,6 +4525,7 @@ class Solution {
- 如果要求只使用 `O(n)` 的时间和额外 `O(1)` 的空间,该怎么做呢?
### 解法
+
分别累加数组中每个元素的二进制中出现的数字,那么出现三次的数字,二进制位上最后累加的结果一定能被 3 整除。不能被 3 整除的位,就属于只出现一次的数字。
```java
@@ -4274,16 +4560,20 @@ class Solution {
}
```
-## 57.1 和为S的两个数字
+## 57.1 和为 S 的两个数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个数组和一个数字 s,在数组中查找两个数,使得它们的和正好是 s。
-如果有多对数字的和等于s,输出任意一对即可。
+如果有多对数字的和等于 s,输出任意一对即可。
你可以认为每组输入中都至少含有一组满足条件的输出。
**样例**
+
```
输入:[1,2,3,4] , sum=7
@@ -4291,6 +4581,7 @@ class Solution {
```
### 解法
+
利用 set 记录元素即可。
```java
@@ -4300,7 +4591,7 @@ import java.util.Set;
class Solution {
/**
* 在数组中找出和为target的两个数
- *
+ *
* @param nums 数组
* @param target 目标和
* @return 满足条件的两个数构成的数组
@@ -4322,14 +4613,18 @@ class Solution {
}
```
-## 57.2 和为S的连续正数序列
+## 57.2 和为 S 的连续正数序列
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个正数 s,打印出所有和为 s 的连续正数序列(至少含有两个数)。
-例如输入 15,由于 `1+2+3+4+5=4+5+6=7+8=15`,所以结果打印出 3 个连续序列 1~5、4~6 和 7~8。
+例如输入 15,由于 `1+2+3+4+5=4+5+6=7+8=15`,所以结果打印出 3 个连续序列 1 ~ 5、4 ~ 6 和 7 ~ 8。
**样例**
+
```
输入:15
@@ -4337,6 +4632,7 @@ class Solution {
```
### 解法
+
用两个指针 `p, q` 指示序列的最小值和最大值。如果序列和大于 s,则从序列中去掉较小的值,即 `++p`;如果序列和小于 s,则序列向右再包含一个数字,即 `++q`。
当 p 超过 s 的一半时,停止。
@@ -4349,7 +4645,7 @@ class Solution {
/**
* 找出和为sum的连续正整数序列
- *
+ *
* @param sum 和
* @return 结果列表
*/
@@ -4389,8 +4685,11 @@ class Solution {
```
## 58.1 翻转单词顺序
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
为简单起见,标点符号和普通字母一样处理。
@@ -4398,6 +4697,7 @@ class Solution {
例如输入字符串 `"I am a student."`,则输出 `"student. a am I"`。
**样例**
+
```
输入:"I am a student."
@@ -4405,13 +4705,14 @@ class Solution {
```
### 解法
+
先对字符串按空格切割成数组,再逆序数组后,最后将元素拼接并返回。
```java
class Solution {
/**
* 翻转单词
- *
+ *
* @param s 字符串
* @return 翻转后的字符串
*/
@@ -4436,8 +4737,11 @@ class Solution {
```
## 58.2 左旋转字符串
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。
请定义一个函数实现字符串左旋转操作的功能。
@@ -4449,6 +4753,7 @@ class Solution {
- 数据保证 n 小于等于输入字符串的长度。
**样例**
+
```
输入:"abcdefg" , n=2
@@ -4456,6 +4761,7 @@ class Solution {
```
### 解法
+
先翻转前 n 个字符,再翻转后面的字符,最后整体翻转。
```java
@@ -4463,7 +4769,7 @@ class Solution {
/**
* 左旋转字符串
- *
+ *
* @param str 字符串
* @param n 左旋的位数
* @return 旋转后的字符串
@@ -4495,8 +4801,11 @@ class Solution {
```
## 59.1 滑动窗口的最大值
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
例如,如果输入数组 `[2, 3, 4, 2, 6, 2, 5, 1]` 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,它们的最大值分别为 `[4, 4, 6, 6, 6, 5]`。
@@ -4506,6 +4815,7 @@ class Solution {
- 数据保证 k 大于 0,且 k 小于等于数组长度。
**样例**
+
```
输入:[2, 3, 4, 2, 6, 2, 5, 1] , k=3
@@ -4513,6 +4823,7 @@ class Solution {
```
### 解法
+
利用双向队列,保证队列头部存放的是最大值的下标,当队列头部下标过期时弹出。
细节:
@@ -4527,7 +4838,7 @@ import java.util.LinkedList;
class Solution {
/**
* 求滑动窗口的最大值
- *
+ *
* @param nums 数组
* @param k 滑动窗口的大小
* @return 最大值构成的数组
@@ -4578,22 +4889,27 @@ class Solution {
```
## 61 扑克牌的顺子
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
-从扑克牌中随机抽 `5` 张牌,判断是不是一个顺子,即这5张牌是不是连续的。
+
+从扑克牌中随机抽 `5` 张牌,判断是不是一个顺子,即这 5 张牌是不是连续的。
`2~10` 为数字本身,`A` 为`1`,`J` 为 `11`,`Q` 为 `12`,`K` 为 `13`,大小王可以看做任意数字。
为了方便,大小王均以 `0` 来表示,并且假设这副牌中大小王均有两张。
-**样例1**
+**样例 1**
+
```
输入:[8,9,10,11,12]
输出:true
```
-**样例2**
+**样例 2**
+
```
输入:[0,8,9,11,12]
@@ -4601,12 +4917,12 @@ class Solution {
```
### 解法
+
- 对数组排序;
- 计算出 0 的个数 `zeroCount`;
- 从第一个不是 0 的数字开始遍历,与后一个数字比较,如果相等,直接返回 `false`;否则累计 `gap`;
- 判断 `zeroCount` 是否大于等于 `gap`。
-
```java
import java.util.Arrays;
@@ -4647,15 +4963,18 @@ class Solution {
}
```
-
## 62 圆圈中最后剩下的数字
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
`0, 1, …, n-1` 这 `n `个数字 `(n>0)` 排成一个圆圈,从数字 `0` 开始每次从这个圆圈里删除第 `m` 个数字。
求出这个圆圈里剩下的最后一个数字。
**样例**
+
```
输入:n=5 , m=3
@@ -4663,7 +4982,9 @@ class Solution {
```
### 解法
+
#### 解法一
+
利用循环数组存放每个数字,每走一步,判断对应位置的数是否是 `-1`,`-1` 的话不计步数,这样一直走 `m` 步。将该位数字置为 `-1`。
当共有 `n-1` 个数被置为 `-1` 时,输出唯一的不为 `-1` 的那个数。
@@ -4716,14 +5037,17 @@ class Solution {
```
#### 解法二
+
我们这样分析:
第一次被删除的圆圈的编号是 `m-1`。那么剩下的数字依次是:
+
```
0 1 2 3 ... m-2 m ... n-1
```
由于下一次(共有 `n-1` 个数)是从 m 开始,因此我们对 m 的编号改为 0,依次改:
+
```
old -> new
@@ -4743,6 +5067,7 @@ m-2 -> n-2
```
我们假设子问题 `x'` 是最终解,那么对应到原问题 `x` 应该是什么呢?
+
```
new -> old
@@ -4768,6 +5093,7 @@ x = (x' + m) % n
```
所以就有一个递推式:
+
```
f(i) = (f(i - 1) + m) % i;
```
@@ -4798,8 +5124,11 @@ class Solution {
```
## 63 股票的最大利润
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖交易该股票可能获得的利润是多少?
例如一只股票在某些时间节点的价格为 `[9, 11, 8, 5, 7, 12, 16, 14]`。
@@ -4807,6 +5136,7 @@ class Solution {
如果我们能在价格为 5 的时候买入并在价格为 16 时卖出,则能收获最大的利润 11。
**样例**
+
```
输入:[9, 11, 8, 5, 7, 12, 16, 14]
@@ -4814,13 +5144,14 @@ class Solution {
```
### 解法
+
遍历到 nums[i] 时,求 nums[i] 与前 i 个数的最小值 `min` 的差值,最后求出最大的差值即可。
```java
class Solution {
/**
* 股票的最大利润
- *
+ *
* @param nums 数组
* @return 最大利润
*/
@@ -4839,12 +5170,16 @@ class Solution {
}
```
-## 64 求1+2+…+n
+## 64 求 1+2+…+n
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
求 `1+2+…+n`,要求不能使用 `乘除法、for、while、if、else、switch、case` 等关键字及条件判断语句 `A?B:C`。
**样例**
+
```
输入:10
@@ -4852,6 +5187,7 @@ class Solution {
```
### 解法
+
利用 Stream API。
```java
@@ -4872,11 +5208,15 @@ class Solution {
```
## 65 不用加减乘除做加法
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、×、÷ 四则运算符号。
**样例**
+
```
输入:num1 = 1 , num2 = 2
@@ -4884,6 +5224,7 @@ class Solution {
```
### 解法
+
先对两数进行异或,求得相加不仅位的结果。再循环对两数进行按位与运算,并左移一位,直至进位为 0。
```java
@@ -4913,13 +5254,17 @@ class Solution {
```
## 66 构建乘积数组
+
来源:[AcWing](https://www.acwing.com/problem/content/15/)
+
### 题目描述
+
给定一个数组 `A[0, 1, …, n-1]`,请构建一个数组 `B[0, 1, …, n-1]`,其中 `B` 中的元素 `B[i]=A[0]×A[1]×… ×A[i-1]×A[i+1]×…×A[n-1]`。
不能使用除法。
**样例**
+
```
输入:[1, 2, 3, 4, 5]
@@ -4931,16 +5276,17 @@ class Solution {
- 能不能只使用常数空间?(除了输出的数组之外)
### 解法
+
把 B 的每个元素 `B[i]` 看成两半的乘积,即 `A[0]xA[1]x...xA[i-1]` 和 `A[i+1]xA[i+2]xA[n-1]`。
-- 对于左半部分:B[i] = B[i - 1] * A[i - 1]
+- 对于左半部分:B[i] = B[i - 1] \* A[i - 1]
```java
class Solution {
-
+
/**
* 构建乘积数组
- *
+ *
* @param A 数组A
* @return 乘积数组B
*/
@@ -4965,4 +5311,4 @@ class Solution {
}
}
-```
\ No newline at end of file
+```
diff --git a/docs/effective-coding.md b/docs/effective-coding.md
index 671a6d3..dd6e0bd 100644
--- a/docs/effective-coding.md
+++ b/docs/effective-coding.md
@@ -1,22 +1,26 @@
# 《Effective Coding——阿里巴巴 Java 开发手册》
## 第一章 编程规约
+
### 命名风格
+
1. 包名统一采用**单数**形式,但是类名如果有复数含义,则类名可以使用复数形式。e.g. `com.alibaba.ai.util.MessageUtils`
### 常量定义
+
1. 如果一个变量值仅在一个范围内变化,则用 enum 类型来定义。
### 代码格式
+
1. `if/for/while/switch/do` 等保留字与括号之间都**必须加空格**。
1. 采用 4 个空格缩进,禁止使用 Tab 控制符。
1. 注释的双斜线与注释内容之间**有且仅有一个空格**。e.g. `// 这是示例注释`
1. 单行字符数不超过 120 个,超出则需要换行,换行遵循:
- - 第二行相对第一行**缩进 4 个空格**,从第三行开始,不再缩进。
- - 运算符与下文一起换行。
- - 方法调用的点符号与下文一起换行。
- - 方法调用的点符号与下文一起换行时,在逗号后进行。
- - 在括号前不要换行。
+ - 第二行相对第一行**缩进 4 个空格**,从第三行开始,不再缩进。
+ - 运算符与下文一起换行。
+ - 方法调用的点符号与下文一起换行。
+ - 方法调用的点符号与下文一起换行时,在逗号后进行。
+ - 在括号前不要换行。
```java
// 正例
@@ -40,17 +44,19 @@ method(args1, args2, args3, ...
1. **没有必要**增加若干空格来使某一行的字符与上一行对应位置的字符对齐。
### OOP 规约
+
1. 对外部正在调用的接口,不允许修改方法签名,以避免对接口调用方产生影响。若接口过时,必须加 `@Deprecated` 注解,并清晰地说明采用的新接口或者新服务是什么。
### 集合处理
+
1. **所有**相同类型的包装类对象之间值的比较,全部使用 equals 方法。
1. 构造方法里面**禁止**加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
1. 慎用 Object 的 clone 方法来拷贝对象。 **说明**:对象的 clone 方法**默认是浅拷贝**,若想实现深拷贝,需要重写 clone 方法。
1. 关于 hashCode 和 equals 的处理,遵循如下规则:
- - 只要重写 equals,就必须重写 hashCode;
- - 因为 Set 存储的是不重复对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
- - 如果自定义对象作为 Map 的键,那么必须重写这两个方法。
- - **说明**:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地将 String 对象作为 key 来使用。
+ - 只要重写 equals,就必须重写 hashCode;
+ - 因为 Set 存储的是不重复对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
+ - 如果自定义对象作为 Map 的键,那么必须重写这两个方法。
+ - **说明**:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地将 String 对象作为 key 来使用。
1. ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 `ClassCastException` 异常。 **说明**:subList 是 ArrayList 的一个视图,对于 subList 子列表的所有操作最终会反映到原列表上。
1. 在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生 `ConcurrentModificationException`。
@@ -88,16 +94,17 @@ str[0] = "bingo";
1. 使用 entrySet 遍历 Map 类集合 K/V,而不是 keySet 方式遍历。如果时 JDK8,使用 Map.foreach() 方法。
1. **高度注意** Map 类集合 K/V 能不能存储 null 值的情况。由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值的,而事实上,存储 null 值时会抛出 NPE 异常。
-| 集合类 | Key | Value | Supper | 说明 |
-|---|---|---|---|---|
-| Hashtable | 不允许为 null | 不允许为 null | Dictionary | 线程安全 |
+| 集合类 | Key | Value | Supper | 说明 |
+| ----------------- | ----------------- | ----------------- | ----------- | ---------- |
+| Hashtable | 不允许为 null | 不允许为 null | Dictionary | 线程安全 |
| ConcurrentHashMap | **不允许**为 null | **不允许**为 null | AbstractMap | 锁分段技术 |
-| TreeMap | 不允许为 null | 允许为 null | AbstractMap | 线程不安全 |
-| HashMap | 允许为 null | 允许为 null | AbstractMap | 线程不安全 |
+| TreeMap | 不允许为 null | 允许为 null | AbstractMap | 线程不安全 |
+| HashMap | 允许为 null | 允许为 null | AbstractMap | 线程不安全 |
11. 利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作。
### 并发处理
+
1. 在创建线程或线程池时,请指定有意义的线程名称,方便出错时回溯。
```java
@@ -108,28 +115,35 @@ public class TimeTaskThread extends Thread {
}
}
```
+
2. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 **说明**:使用线程池的好处是减少在创建和销毁线程上所消耗的时间及系统资源,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大流量同类线程而导致消耗完内存或者“过度切换”的问题。
1. 在对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则**可能会造成死锁**。 **说明**:如果线程一需要对表 A/B/C 依次加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A/B/C,否则可能出现死锁。
1. volatile 解决多线程内存不可见问题。对于一写多读,可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
### 控制语句
+
1. 在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,**都必须**包含一个 default 语句并且放在最后,即使它什么代码都没有。
1. 在高并发场景中,**避免使用**“等于”判断作为中断或退出的条件。 **说明**:如果并发控制没有处理好,容易产生等值判断被击穿的情况,应使用大于或小于的区间判断条件来代替。
1. 不要在条件判断中执行其它复杂的语句,可将复杂逻辑判断的结果赋值给一个**有意义的布尔变量名**,以提高可读性。
### 注释规约
+
1. 特殊注释标记。TODO 实际上是一个 Javadoc 的标签,虽然目前的 Javadoc 还没有实现,但已经被广泛使用,且**只能应用于类、接口和方法上**。在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正。
### 其他
+
1. 注意 Math.random() 这个方法返回的是 double 类型,取值范围 x ∈ [0, 1),如果想获得整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
## 第二章 异常日志
+
### 异常处理
+
1. catch 时请分清稳定代码和不稳定代码。稳定代码指的是无论如何都不会出错的代码。对于非稳定代码的 catch,尽可能在进行异常类型的区分后,再做对应的异常处理。
1. 不要在 finally 块中使用 return。 **说明**:当 finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
1. 定义时区分 unchecked/checked 异常,避免直接抛出 new RuntimeException(),更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如 DAOException/ServiceException 等。
### 日志规约
+
1. 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API。使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
```java
@@ -142,31 +156,37 @@ private static final Logger logger = LoggerFactory.getLogger(Abc.class);
2. 谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用 warn 记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并及时删除这些观察日志。
## 第三章 单元测试
+
1. 单元测试是可重复执行的,不能受到外界环境的影响。
1. 和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。
1. 单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项目提测前完成单元测试。
## 第四章 安全规约
+
1. 针对发帖、评论、发送即时消息等用户生成内容的场景,必须实现防刷、文本内容违禁词过滤等风控策略。
## 第五章 MySQL 数据库
+
### 建表规约
+
1. 表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型为 `unsigned tinyint`。 **说明**:任何字段如果为非负数,则必须是 unsigned。
1. 字段允许适当冗余,以提高查询性能,但必须考虑数据一致。e.g. 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,**避免关联查询**。冗余字段遵循:
- - 不是频繁修改的字段;
- - 不是 varchar 超长字段,更不能是 text 字段。
+ - 不是频繁修改的字段;
+ - 不是 varchar 超长字段,更不能是 text 字段。
### 索引规约
+
1. 在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。
1. 页面搜索严禁左模糊或者全模糊,如果需要请通过搜索引擎来解决。 **说明**:索引文件具有 B-Tree 的**最左前缀匹配特性**,如果左边的值未确定,那么无法使用此索引。
1. 如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
- - **正例**:where a=? and b=? order by c; 索引: a_b_c。
- - **反例**:索引中有范围查找,那么索引有序性无法利用,如 WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
+ - **正例**:where a=? and b=? order by c; 索引: a_b_c。
+ - **反例**:索引中有范围查找,那么索引有序性无法利用,如 WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
1. 利用延迟关联或者子查询优化超多分页场景。 **说明**:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 的行,返回 N 行。当 offset 特别大的时候,效率会非常的低下,要么控制返回的总页数,要么对超过阈值的页数进行 SQL 改写。
1. 建组合索引的时候,区分度最高的在最左边。
1. SQL 性能优化的目标,至少要达到 range 级别,要求是 ref 级别,最好是 consts。
### SQL 语句
+
1. 不要使用 count(列名) 或 count(常量) 来替代 count(\*),count(\*) 是 SQL92 定义的标准统计行数的语句,跟数据库无关,跟 NULL 和非 NULL 无关。 **说明**:count(\*) 会统计值为 NULL 的行,而 count(列名) 不会统计此列为 NULL 值的行。
1. `count(distinct column)` 计算该列除 NULL 外的不重复行数。注意,`count(distinct column1,column2)` 如果其中一列全为 NULL,那么即使另一列用不同的值,也返回为 0。
1. 当某一列的值全为 NULL 时,`count(column)` 的返回结果为 0,但 `sum(column)` 的返回结果为 NULL,因此使用 sum() 时需注意 NPE 问题。 可以使用如下方式来避免 sum 的 NPE 问题。
@@ -181,27 +201,33 @@ SELECT IF(ISNULL(SUM(g), 0, SUM(g))) FROM table;
1. `in` 操作能避免则避免。若实在避免不了,需要仔细评估 in 后面的集合元素数量,控制在 1000 个之内。
### ORM 映射
-1. POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性的映射。
-1. `sql.xml` 配置参数使用:`#{}, #param#`,不要使用 ${},此种方式容易出现 SQL 注入。
+
+1. POJO 类的布尔属性不能加 is,而数据库字段必须加 is\_,要求在 resultMap 中进行字段与属性的映射。
+1. `sql.xml` 配置参数使用:`#{}, #param#`,不要使用 \${},此种方式容易出现 SQL 注入。
1. `@Transactional` 事务不要滥用。事务会影响数据库的 QPS。另外,使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
## 第六章 工程结构
+
### 应用分层
+
1. 在 DAO 层,产生的异常类型有很多,无法用细粒度的异常进行 catch,因此使用 `catch(Exception e)` 方式,并 throw new `DAOException(e)`,不需要打印日志,因为日志在 Manager/Service 层,一定需要捕获并写到日志文件中去。如果同台服务器再写日志,会浪费性能和存储。
### 二方库依赖
+
1. 定义 GAV 遵从以下规则:
- - GroupID 格式:com.{公司/BU}.业务线.\[子业务线\],最多 4 级。e.g. `com.taobao.jstorm`
- - ArtifactID 格式:产品线名-模块名。语义不重复不遗漏。e.g. `dubbo-client、fastjson-api、jstorm-tool`
- - Version 格式:主版本号.次版本号.修订号。
+ - GroupID 格式:com.{公司/BU}.业务线.\[子业务线\],最多 4 级。e.g. `com.taobao.jstorm`
+ - ArtifactID 格式:产品线名-模块名。语义不重复不遗漏。e.g. `dubbo-client、fastjson-api、jstorm-tool`
+ - Version 格式:主版本号.次版本号.修订号。
1. 线上应用不要依赖 SNAPSHOT 版本。 **说明**:不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。
### 服务器
+
1. 高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 **说明**:操作系统默认 240s 后才会关闭 time_wait 状态的连接。在高并发访问下,服务器端会因为处于 time_wait 的连接数过多,而无法建立新的连接,所以需要在服务器上调小此等待值。
1. 给 JVM 设置 `-XX:+HeapDumpOnOutOfMemoryError` 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。 **说明**:OOM 的发生是有概率的,甚至有规律地相隔数月才出现一例,出现时的现场信息对查错非常有价值。
1. 在线上生产环境,JVM 的 Xms 和 Xms 设置一样大小的内存容量,避免在 GC 后调整堆大小带来的压力。
## 第七章 设计规约
+
1. 谨慎使用继承的方式进行扩展,优先使用**聚合或组合**的方式来实现。 **说明**:若一定要继承,则必须符合里氏代换原则。此原则要求在父类能够出现的地方子类一定能够出现。
1. 在系统设计时,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护。
-1. 注意对扩展开放,对修改闭合。
\ No newline at end of file
+1. 注意对扩展开放,对修改闭合。
diff --git a/docs/effective-java.md b/docs/effective-java.md
new file mode 100644
index 0000000..90301f2
--- /dev/null
+++ b/docs/effective-java.md
@@ -0,0 +1,1117 @@
+# 《Effective Java》
+
+## 第二章 创建和销毁对象
+
+### 第 1 条:考虑用静态工厂方法代替构造器
+
+静态工厂方法相比构造器,优势有以下几个:
+
+1. 静态工厂方法有名称,能更确切地描述正被返回的对象,更易于阅读。构造器方法名称都是固定的,只能通过改变参数列表来构造不同对象。
+1. 不必在每次调用时都创建一个对象,可以先将对象缓存起来,需要时直接返回,避免创建不必要的重复对象。比较时可以直接用 `==` 操作符。
+1. 可以返回原返回类型的任何子类对象,更加灵活。适用于基于接口的框架。
+1. 在创建参数化实例时,代码更加简洁。
+
+不需要接连两次提供类型参数:
+
+```java
+Map> m = new HashMap>();
+```
+
+只需要提供一个静态工厂方法:
+
+```java
+public static Hash newInstance() {
+ return new HashMap;
+}
+
+Map> m = HashMap.newInstance();
+```
+
+但是,静态工厂方法也有一些缺点:
+
+1. 类如果只包含私有构造器,那么就不能被子例化(继承)。但这样也许也会因祸得福,因为它鼓励使用复合,而不是继承;
+1. 静态工厂方法与其它静态方法没什么区别,无法像构造器一样在 API 文档中明确标识出来。但是,静态工厂方法有一些惯用名称,如 `valueOf`, `of`, `getInstance`, `newInstance`......
+
+### 第 2 条:遇到多个构造器参数时要考虑用构建器
+
+考虑用一个类表示包含食品外面显示的营养成分标签。这些标签有几个域是必需的,还有超过 20 个可选域。大多数产品在某几个可选域中都会有非零的值。
+
+对于这样的类,应该用哪种构造器或者静态方法来编写呢?
+
+1. 重叠构造器模式
+
+第一种方式是**重复构造器模式**。先提供一个只有必要参数的构造器,再提供一个有一个可选参数的构造器,接着是两个可选参数的构造器,依次类推,最后一个构造器包含所有可选参数。
+
+```java
+public class NutritionFacts {
+ private final int servingSize;
+ private final int servings;
+ private final int calories;
+ private final int fat;
+ private final int sodium;
+ private final int carbohydrate;
+
+ public NutritionFacts(int servingSize, int servings) {
+ this(servingSize, servings, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings, int calories) {
+ this(servingSize, servings, calories, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings, int calories, int fat) {
+ this(servingSize, servings, calories, fat, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
+ this(servingSize, servings, calories, fat, sodium, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
+ this.servingSize = servingSize;
+ this.servings = servings;
+ this.calories = calories;
+ this.fat = fat;
+ this.sodium = sodium;
+ this.carbohydrate = carbohydrate;
+ }
+}
+```
+
+想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:
+
+```java
+NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
+```
+
+这个构造器通常需要许多你本不想设置的参数,但还是不得不为它们传递值。随着参数数目的增多,很快就会失去了控制。客户端代码也会很难编写,可读性也不好。
+
+2. JavaBean 模式
+
+第二种模式是 **JavaBean** 模式,在这种模式下,创建一个无参构造器来创建对象,然后调用 setter 方法设置每个必要的参数。这种模式,弥补了重叠构造器模式的不足,代码读起来也很容易,很多读者应该都很熟悉了。
+
+```java
+NutritionFacts cocaCola = new NutritionFacts();
+cocaCola.setServingSize(200);
+cocaCola.setServings(8);
+cocaCola.setCalories(100);
+cocaCola.setSodium(35);
+cocaCola.setCarbohydrate(27);
+```
+
+遗憾的是,JavaBean 模式自身有很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中 JavaBean 可能处于不一致的状态。若试图使用处于不一致状态的对象,将会导致失败,调试起来也十分困难。程序员需要付出额外的努力来确保它的线程安全。
+
+3. Builder 模式
+
+有第三种替代方法,既能保证像重叠构造器模式那样的安全性,也能保证像 JavaBean 模式一样,有很好的可读性,就是 **Builder** 模式。
+
+```java
+public class NutritionFacts {
+ private final int servingSize;
+ private final int servings;
+ private final int calories;
+ private final int fat;
+ private final int sodium;
+ private final int carbohydrate;
+
+ public static class Builder {
+ // Required params
+ private final int servingSize;
+ private final int servings;
+
+ // Optional params
+ private int calories = 0;
+ private int fat = 0;
+ private int sodium = 0;
+ private int carbohydrate = 0;
+
+ public Builder(int servingSize, int servings) {
+ this.servingSize = servingSize;
+ this.servings = servings;
+ }
+
+ public Builder calories(int val) {
+ this.calories = val;
+ return this;
+ }
+ public Builder fat(int val) {
+ this.fat = val;
+ return this;
+ }
+ public Builder sodium(int val) {
+ this.sodium = val;
+ return this;
+ }
+ public Builder carbohydrate(int val) {
+ this.carbohydrate = val;
+ return this;
+ }
+ public NutritionFacts build() {
+ return new NutritionFacts(this);
+ }
+ }
+
+ // 私有构造器
+ private NutritionFacts(Builder builder) {
+ servingSize = builder.servingSize;
+ servings = builder.servings;
+ calories = builder.calories;
+ fat = builder.fat;
+ sodium = builder.sodium;
+ carbohydrate = builder.carbohydrate;
+ }
+}
+```
+
+客户端代码就很容易编写了,更为重要的是,易于阅读。
+
+```java
+NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
+ .calories(100)
+ .sodium(35)
+ .carbohydrate(27)
+ .build();
+```
+
+Builder 模式也有它自身的不足。为了创建对象,必须先创建它的构建器。虽然创建构建器的开销在实践种可能不那么明显,但是在某些十分注重性能的情况下,可能就成了问题了。Builder 模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如 4 个或者更多个参数。
+
+简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder 模式就是种不错的选择,特别是当大多数参数都是可选的时候。它较传统的重叠构造器模式相比,更易于阅读;而较 JavaBean 模式,更加安全。
+
+### 第 3 条:用私有构造器或者枚举类型强化 Singleton 属性
+
+1. 公有静态成员
+
+```java
+public class Singleton {
+ public static final Singleton INSTANCE = new Singleton();
+ private Singleton() {}
+}
+```
+
+2. 静态工厂方法
+
+```java
+public class Singleton {
+ private static final Singleton INSTANCE = new Singleton();
+ private Singleton() {}
+
+ public static Singleton getInstance() {
+ return INSTANCE;
+ }
+}
+```
+
+这两种方法都能保证 Singleton 的全局唯一性。但是,享有特权的客户端可以借助 `AccessibleObject.setAccessible` 方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
+
+为了使利用这其中一种方法实现的 Singleton 类变成可序列化的,仅仅在声明中加上 `implements Serializable` 是不够的。为了维护并保证 Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个 `readResolve` 方法。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例。
+
+```java
+priavte Object readResolve() {
+ return INSTANCE;
+}
+```
+
+3. 单元素枚举
+
+Java 1.5 版本开始,实现 Singleton 有了第三种方法。只需编写一个包含单个元素的枚举类型:
+
+```java
+public enum Singleton {
+ INSTANCE;
+
+ public void otherMethods() {...}
+}
+```
+
+这种方法更加简洁,无偿提供了序列化机制,绝对防止多次实例化,是实现 Singleton 的最佳方式。
+
+### 第 4 条:通过私有构造器强化不可实例化的能力
+
+我们在项目开发过程中,有时候肯定会遇到一些工具类,我们不希望它们被实例化,因为它们的方法可能都被 `static` 来修饰,所以实例对它们没有任何的意义;然而我们在编码的过程中,可能往往对一些工具类的编写都没有注意,没有去写构造方法,这时候,在缺少显式的构造器的情况下,编译器会提供一个共有的、无参的缺省构造器(`default constructor`)。对于用户而言,这个构造器和其它的构造器没有任何区别。所以在一些已发行的 API 里面我们经常会看到一些被无意识实例化的类。
+
+**企图通过讲类做成抽象类来强制该类不可被实例化,这是行不通的。** 因为抽象类可以被子类化,子类也可以被实例化。同时定义为抽象类,还会误导用户,以为这种类是专门为了继承而设计的。那怎样才可以确保类不被实例化呢,因为只有当类不包含显示的构造器时,编译器才会生成缺省的构造器,所以我们只要给这个类构建私有的构造器,它就不会被实例化了:
+
+```java
+// Noninstantiable utility class
+public class UtilityClass {
+ // Suppress default constructor for noninstantiability
+ private UtilityClass() {
+ throw new AssertionError();
+ }
+
+ ... // Remainder omitted
+}
+```
+
+如上,由于显示的构造器是私有的,则不会在类的外部被实例化。 AssertionError 不是必须的,但是这样写,可以避免在类的内部调用构造器。它保证该类在任何情况下都不会被实例化。
+
+**注意** 这种用法也有副作用,它使得一个类不能被子类化。因为所有的构造器都必须显式或隐式地调用超类(superclass)构造器,在上面这种情况下,子类就没有可访问的超类构造器去调用了。
+
+### 第 5 条:避免创建不必要的对象
+
+一般来说,最好不要在每次需要的时候都创建一个功能相同的新对象而是重用对象。重用方式既快速,又流行。如果对象是不可变的(immutable),它就始终可以被重用。
+下面举一个极端的反面例子,考虑下面的语句:
+
+```java
+
+String s = new String("stringette"); // Don't do this!
+
+```
+
+上面的语句每次被执行的时候都会创建一个新的 String 实例,但是这些创建对象的动作全都是不必要的。传递给 String 构造器的参数("stringette")本身就是一个 String 实例,功能方面等同于构造器创建的所有对象。想一想 ,如果该用法在一个循环中,或者是在一个被频繁调用的方法中,就会创建出成千上万个不必要的 String 实例。
+
+改进后的版本如下所示:
+
+```java
+
+String s = "stringette";
+
+```
+
+上面版本只用了一个 String 实例,而不是每次执行的时候都创建一个新的 String 实例。并且,它还可以保证,对于所有在同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用[JLS,3.10.5]。
+
+对于同时提供了静态工厂方法(见第 1 条)和构造器的不可变类,通常应该使用静态工厂方法而不是构造器,这样可以避免创建不必要的对象。构造器在每次被调用的时候都会创建一个新的对象,而静态工厂方法重来不要求这样做,实际上也不会这么做。
+
+除了重用不可变对象之外,也可以重用那些已知不会被修改的可变对象。下面通过我们熟悉的可变 Date 对象来实现一个比较微妙、也比较具体的反面例子,由于 Date 对象一旦计算出来之后就不再改变。
+
+```java
+
+public class Person {
+
+ private final Date birthDate;
+
+ public Person(Date birthDate) {
+ this.birthDate = birthDate;
+ }
+
+ // Other fields, methods, and constructor omitted
+ // Don't do this!
+ public boolean isBabyBoomer() {
+ // Unnecessary allocation of expensive object
+ Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
+ Date boomStart = gmtCal.getTime();
+ gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
+ Date boomEnd = gmtCal.getTime();
+
+ return birthDate.compareTo(boomStart) >= 0 &&
+ birthDate.compareTo(boomEnd) < 0;
+ }
+
+}
+
+```
+
+上面的类建立了一个模型:其中有一个人,并有一个 isBabyBoomer 方法,用来检验这个人是否为一个 “baby boomber(生育高峰期出生的小孩)” ,相当于就是检测这个人是否出生于 1946 年至 1964 年之间。
+
+通过上述代码可以发现,isBabyBoomer 方法每次都调用的时候,都会创建一个新的 Calendar、一个 TimeZone 和两个 Date 实例,这其实是不必要的。下面我们通过一个改进的版本,用一个静态的初始化器(`initializer`),避免了这种效率低下的情况:
+
+```java
+
+public class Person {
+ private final Date birthDate;
+
+ public Person(Date birthDate) {
+ this.birthDate = birthDate;
+ }
+ // Other fields, methods, and constructor omitted
+
+ // The starting and ending dates of the baby boom
+ private static final Date BOOM_START;
+ private static final Date BOOM_END;
+
+ static {
+ Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
+ BOOM_START = gmtCal.getTime();
+ gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
+ BOOM_END = gmtCal.getTime();
+ }
+
+ public boolean isBabyBoomer() {
+ return birthDate.compareTo(BOOM_START) >= 0 &&
+ birthDate.compareTo(BOOM_END) < 0;
+ }
+}
+
+```
+
+改进后的 Person 类只在初始化的时候创建 Calendar、 TimeZone 和 Date 实例一次,而不是在每次调用 isBabyBoomer 的时候都会创建这些实例。如果 isBabyBoomer 方法被频繁地调用,改进后的方法将会显著的提高性能。就比如我们要检查 1000 万人是否出生在 1946 年和 1964 年之间,经过测试,原来的版本需要 32000ms,而改进后的只需要 130ms,大约快了 250 倍。但是这种优化带来的效果不总是那么明显,因为 Calendar 实例的创建代价特别昂贵。但是改进后的版本在数据量大的情况下就会有明显的性能提升,并且代码更加的清晰,因为 BOOM_START 和 BOOM_END 很明显应该被作为常量来对待。
+
+在本条目前面的例子中,所讨论到的对象显然都是能够被重用的,因为它们被初始化后就不会再改变。其它有些情形则并不总是那么明显了。考虑适配器(`adapter`)的情形,有时也叫做视图(`view`)。适配器是指这样一个对象:它把功能委托给一个后备对象(`backing object`),从而为后备对象提供一个可以替代的接口。由于适配器除了后备对象之外,没有其它的状态信息,所以针对某个给定对象的特定适配器而言,它不需要创建多个适配器实例。
+
+例如,Map 接口的 keySet 方法返回该 Map 对象的 Set 视图,其中包含该 Map 中所有的键(`key`)。表面看起来,好像每次调用 keySet 都应该创建一个新的 Set 实例,但是,对于一个给定的 Map 对象,实际上每次调用 keySet 方法都会返回同样的 Set 实例。虽然被返回的 Set 实例一般是可改变的,但是所有返回的对象在功能上是等同的:当其中一个返回对象发生变化的时候,所有其它的返回对象也要发生变化,因为它们是有同一个 Map 实例支撑的。虽然创建 keySet 视图对象的多个实例并无害处,却也是没有必要的。
+
+在 Java 1.5 发行版本中,有一种创建多余对象的新方法,称为自动装箱(`autoboxing`),它允许程序员将基本类型和装箱基本类型(`Boxed Primitive Type`)混用,按需要自动装箱和拆箱。自动装箱使得基本类型和装箱基本类型的差别变得很模糊,但是并没有完全消除。它们在语义上有着微妙的差别,在性能上也有着比较明显的差别(见第 49 条)。考虑下面的程序,它计算所有 int 正值的总和。为此,程序必须使用 long 类型,因为 int 不够大,无法容纳所有 int 正值的总和:
+
+```java
+
+// Hideously slow program! Can you spot the object creation?
+public static void mian(String[] args) {
+ Long sum = 0L;
+ for (long i = 0; i < Integer.MAX_VALUE; i++) {
+ sum += i;
+ }
+ System.out.println(sum);
+}
+
+```
+
+这段程序程序算出的答案是正确的,但是比实际情况要更慢一些,只因为打错一个字符。变量 sum 被声明成 Long 而不是 long,意味着程序构造了大约 2^31 个多余的 Long 实例(大约每次往 Long sum 中增加 long 时构造一个实例)。将 sum 的声明从 Long 改成 long,运行时间从 43 秒减少到 6.8 秒。结论很明显:**要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。**
+
+当然,我们也不要错误地认为本条目所介绍的内容暗示着“创建对象的代价非常昂贵,我们应该尽可能地避免创建对象”。相反,由于小对象的构造器只做很少量的显式工作,所以,小对象的创建和回收动作是非常廉价的,特别是在现代的 JVM 实际上更是如此。通过创建附加的对象,提升程序的清晰性、简洁性和功能性,这通常是件好事。
+
+反之,通过维护自己的对象池(`object pool`)来避免创建对象并不是一种好的做法,除非池中的对象是非常重量级的。真正正确使用对象池的典型对象示例就是数据库连接池。建立数据库连接的代价是非常昂贵的,因此重用这些对象是非常有意义。而且,数据库的许可可能限制你只能使用一定数量的连接。但是,一般而言,维护自己的对象池必定会把代码弄得很乱,同时增加内存占用(`footprint`),并且还会损害性能。所以我们要慎用对象池。
+
+与本条目对应的是第 39 条中有关的“保护性拷贝(`defensive copying`)”的内容。本条目提及“当你应该重用现有对象的时候,请不要创建新的对象”,而第 39 条则说“当你应该创建新的对象的时候,请不要重用现有的对象”。注意,在提倡使用保护性拷贝的时候,是因为重用对象而付出的代价要远远大于因创建对象而付出的代价。必要时如果没能实施保护性拷贝,会导致潜在的错误和安全漏洞;而不必要的创建对象则只会影响程序的风格和性能。
+
+**总结来说,就是应该按情况具体分析,该创建对象还是重用对象;通过分析,我们应该知道没有保护的重用对象,需要特别注意,不然可能会导致错误和安全漏洞。**
+
+### 第 6 条:消除过期的对象引用
+
+当你从手工管理内存的语言(比如 C 或 C++)转换到具有垃圾回收功能的语言(比如 Java 或 Go)的时候,程序员的工作会变得更加的容易,因为当你用完了对象之后,它们会被自动回收。当你由 C 或 C++ 语言转换到 Java 编程语言第一次经历对象回收功能的时候,会觉得有点不可思议。这很容易给你留下不需要自己考虑内存管理的印象,其实不然。
+
+考虑下面这个简单的栈实现的例子:
+
+```java
+
+// Can you spot the "memory leak"
+public class Stack {
+ private Object[] elements;
+ private int size = 0;
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ public Stack() {
+ elements = new Object[DEFAULT_INITIAL_CAPACITY];
+ }
+
+ public void push(Object e) {
+ ensureCapacity();
+ elements[size++] = e;
+ }
+
+ public Object pop() {
+ if (size == 0)
+ throw new EmptyStackException();
+ return elements[--size];
+ }
+
+ /**
+ * Ensure space for at least one more element, roughly
+ * doubling the capacity each time the array needs to grow.
+ */
+ private void ensureCapacity() {
+ if (elements.length == size) {
+ elements = Arrays.copyOf(elements, 2 * size + 1);
+ }
+ }
+
+}
+
+```
+
+这段程序(它的泛型版本请见第 26 条)中并没有很明显的错误。但是这个程序中隐藏着一个问题。不严格地讲,这段程序有一个“内存泄漏”,随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来。在极端的情况下,这种内存泄漏会导致磁盘交换(`Disk Paging`),甚至导致程序失败(`OutOfMemoryError`错误),但是这种失败情形相对比较少见。
+
+那么,程序中在哪里发生了泄漏呢?如果一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。这是因为,栈内部维护着对这些对象的过期引用(`Obsolete refence`)。所谓的过期引用,是指永远也不会再被解除的引用。在本例中,凡是在 elements 数组的“活动部分(`active portion`)”之外的任何引用都是过期的。活动部分是指 elements 中小标小于 size 的那些元素。
+
+在支持垃圾回收的语言中,内存泄漏是非常隐蔽的(称这类内存泄漏为“无意识的对象保持(`unintentional object retention`)“更为恰当)。如果一个对象引用被无意识的保留起来了,那么,垃圾回收机制不仅不会处理这个对象,而且也不会处理被这个对象所引用的所有其它对象。即使只有少量的几个对象被无意识的保留下来,也会有许许多多的对象排除在垃圾回收机制之外,从而对性能造成潜在的重大影响。
+
+这类问题的修复方法很简单:一旦对象引用已经过期,只要清空这些引用即可。对于上述的 Stack 类而言,只要一个单元被弹出栈,指向它的引用就已经过期了。pop 方法的改进版如下:
+
+```java
+
+public Object pop() {
+ if (size == 0)
+ throw new EmptyStackException();
+ elements[size] = null; // Eliminate obsolete reference
+ return elements[--size];
+}
+
+```
+
+清空过期引用的另一个好处是,如果它们以后又被错误的解除引用,程序就会立即抛出 NullPointerException 异常,而不是悄悄地错误运行下去。尽快的检测出程序中的错误往往是有益的。
+
+当程序员第一次被类似这样的问题困扰的时候,他们往往会过分小心:对于每一个对象引用,一旦程序不再用到它,就把它清空。其实这样做即没必要,也不是我们所期望的,因为这样做会把我们的程序弄的很乱。**清空对象引用应该是一种例外,而不是一种规范行为** 。消除过期引用最好的方法是让包含该引用的变量结束其生命周期。如果你是在最紧凑(最小)的作用域范围定义每一个变量(见第 45 条),这种情形就会自然而然的发生。
+
+那么,何时应该清空引用呢?Stack 类的哪方面特性使它易于遭受内存泄漏的影响呢?简而言之,问题在于,Stack 类自己管理内存(`manage its own memory`)。存储池(`storage pool`)包含了 elements 数组(对象应用单元,而不是对象本身)的元素。数组活动区域(同前面的定义)中的元素是已分配的(`allocated`),而数组其余部分的元素则是自由的(`free`)。但是垃圾回收器并不知道这一点;对于垃圾回收器而言,elements 数组中所有的对象引用都同等有效。只有程序员知道数组的非活动部分是不重要的。程序员可以把这个情况告知垃圾回收器,做法很简单:一旦数组元素变成了非活动部分的一部分,程序员就手工清空这些数组元素。
+
+一般而言,**只要类是自己管理内存,程序员就应该警惕内存泄漏问题**。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
+
+**内存泄漏的另一个常见来源是缓存**。一旦你把对象引用放到缓存中,它就很容易被遗忘掉,从而使用它不再有用很长一段时间内仍然留在缓存中。对于这个问题,有几种可能的解决方案。如果你正好要实现这样的缓存:只要在缓存之外存在对某个项的键的引用,该项就有意义,那就可以用 WeakHashMap 代表缓存;当缓存中的项过期之后,它们就会自动被删除。记住只有当所要的缓存项的生命周期是由该键的外部引用而不是由键的值决定时候,WeakHashMap 才有用处。
+
+更为常见的情形则是,“缓存项的生命周期是否有意义”并不是很容易确定,随着时间的推移,其中的项的价值变得越来越没有价值。在这种情况下,缓存应该时不时的清空掉无用的项。清除工作可以由一个后台线程(可能是 Timer 或者 ScheduledThreadPoolExecutor)来完成,或者也可以给缓存添加新数据的时候顺便进行清理。LinkedHashMap 利用其 removeEldestEntry 方法可以很容易地实现后一种方案。对于更加复杂的缓存,必须直接使用 java.lang.ref。
+
+**内存泄漏的第三个常见来源是监听器和其它回调**。如果你实现了一个 API,客户端在这个 API 中注册回调,却没有显示地取消注册,那么除非你采取某些动作,否则它们就会聚集。确保回调立即被当作垃圾回收的最佳方法是只保存它们的软引用(`weak reference`),例如,只将它们保存成 WeakHashMap 中的键。
+
+由于内存泄漏通常不会表现成明显的失败,所以它们可以在一个系统中存在很多年。往往只有通过代码检查,或者借助于 Heap 剖析工具(`Heap Profiler`)才可以发现内存泄漏问题。所以,如果我们能在内存泄漏发生之前就知道如何预测和分析此类问题,并预防和阻止它们发生,那是最好不过了。
+
+### 第 7 条:避免使用终结方法
+
+**终结方法(`finalizer`)通常是不可预测的,也是很危险的,一般情况下是不必要的**。使用终结方法会有很多缺点:会导致行为不稳定、降低性能,以及可移植性问题。当然,终结方法也有其可用之处,该条目后面会进行介绍;但是根据经验,还是应该避免使用终结方法。
+
+C++ 程序员被告知“不要把终结方法当作是 C++ 中析构器(`destructors`)的对应物”。在 C++ 中,析构器是回收一个对象所占用资源的常规方法,是构造器所必须的对应物。在 Java 中,当一个对象变得不可到达时,垃圾回收器会回收与该对象相关联的存储空间,并不需要程序员做专门的工作。C++ 中的析构器也可以被用来回收其他的内存资源。而在 Java 中,一般用 try-finally 块来完成类似的工作。
+
+终结方法的缺点在于不能保证会被及时地执行[JLS,12.6]。从一个对象变得不可到达开始,到它的终结方法被执行,所花费的这段时间是任意长的。这意味着,注重时间(`time-critical`)的任务是不应该由终结方法来完成。例如,用终结方法来关闭已经打开的文件,这是严重错误,因为打开文件的描述符是一种有限的资源。由于 JVM 会延时执行终结方法,所以大量的文件会保留在打开状态,当一个程序再不能打开文件的时候,它可能会运行失败。
+
+及时地执行终结方法正是垃圾回收算法的一个主要功能,这种算法在不同的 JVM 实现中会大相径庭。如果程序依赖于终结方法被执行的时间点,那么这个程序在不同的 JVM 运行的表现可能会截然不同。一个程序可能在你测试的 JVM 平台上运行的非常好,但是在你最重要顾客的 JVM 平台上却根本无法运行,这是完成可能的。
+
+延迟终结过程并不只是一个理论问题。在很少见的情况下,为类提供终结方法,可能会随意地延迟其实例的回收过程。一位同事最近在调试一个长期运行的 GUI 应用程序的时候,该应用程序莫名其妙地出现 OutOfMemoryError 错误而死掉。分析表明,该应用程序死掉的时候,其终结方法队列中有数千个图像对象正在被等待终结和回收。遗憾的是,终结方法线程的优先级比该程序的其它线程的要低很多,所以,图形对象的终结速度达不到进入队列的速度。Java 语言规范并不保证哪个线程将会执行终结方法,所以,除了不使用终结方法以外,并没有很轻便的办法能够避免这样的问题。
+
+Java 语言规范不仅不保证终结方法会被及时地执行,而且根本就不保证它们会被执行。当一个程序终止的时候,某些已经无法访问的对象上的终结方法却根本没有被执行,这是完全有可能的。结论是:**不应该依赖终结方法来更新重要的持久状态**。例如,依赖终结方法来解释共享资源(比如数据库)上的永久锁,很容易让整个分布式系统垮掉。
+
+不要被 System.gc 和 System.runFinalization 这两个方法所诱惑,它们确实增加了终结方法被执行的机会,但是它们并不保证终结方法一定会被执行。唯一声称保证终结方法被执行的方法是 System.runFinalizersOnExit,以及它臭名昭著的孪生兄弟 RunTime.runFinalizersOnExit。这两个方法都有致命的缺陷,已经被废弃了[ThreadStop]。
+
+当你并不确定是否应该避免使用终结方法的时候,这里还有一种值得考虑的情形:如果未被捕获的异常在终结方法中被抛出来,那么这种异常可以被忽略,并且该对象的终结过程也会终止[JLS,12.6]。未被捕获的异常会使对象处于破坏的状态(a corrupt state),如果另一个线程企图使用这种被破坏的对象,则可能发生任何不确定的行为。在正常情况下,未被捕获的异常会使线程终止,并打印出栈轨迹(`Stack Trace`),但是,如果发生在终结方法之中,则不会如此,甚至连警告都不会打印出来。
+
+还有一点:**使用终结方法有一个非常严重的(`Severe`)性能损失**。在我的机器上,创建和销毁一个简单对象的时间大约是 5.6ns,增加一个终结方法使时间增加到了 2400ns。换句话说,用终结方法创建或销毁对象大约慢了 430 倍。
+
+那么,如果类中封装的资源(例如文件或者线程)确实需要终止,应该怎么做才能不编写终结方法呢?只需**提供一个显示的终止方法**,并要求该类的客户端在每个实例不再有用的时候调用这个方法。值得提及的一个细节是,该实例必须记录下自己是否已经被终结了:显示的终止方法必须在一个私有域中记录下“该对象已经不再有效”。如果这些方法是在对象已经被终止之后调用,其它的方法就必须检查这个域,并抛出 IllegalStateException 异常。
+
+显示终止方法的典型例子是 InputStream、OutputStream 和 java.sql.Connection 上的 close 方法。另一个例子是 java.utils.Timer 上的 cancel 方法,它执行必要的状态改变,使得与 Timer 实例相关联的该线程温和地终止自己。java.awt 中的例子还包括 Graphics.dispose 和 Window.dispose。这些方法通常由于性能不好而不被人们关注。一个相关的方法是 Image.flush,它会释放所有与 Image 实例相关的资源,但是该实例仍然处于可用的状态,如果有必要的话,会重新分配资源。
+
+**显示的终止方法通常与 try-finally 结构联合起来使用,以确保及时终止**。在 finally 子句内部调用显示的终止方法,可以确保即使在使用对象的时候有异常抛出,该终止方法也会执行:
+
+```java
+
+// try-finally block guarantees execution of termination method
+Foo foo = new Foo(...);
+try {
+ // Do what must be done with foo
+ ...
+} finally {
+ foo.terminate(); // Explicitt termination method
+}
+
+```
+
+那么终结方法有什么好处呢?它们有两种合法用途。第一种用途是,当对象的所有者忘记调用前面段落建议的显示终止方法时,终结方法可以充当“安全网(`safety net`)”。虽然这样做并不是保证终结方法会被及时的调用,但是在客户端无法通过调用显式的终止方法来正常结束操作的情况下(希望这种情况尽可能少地发生),迟一点释放关键资源总比永远不释放要好。但是如果终结方法发现资源还未被终止,则应该在日志中记录一条警告,因为这表示客户端代码中的一个 Bug,应该得到修复。如果你正考虑编写这样的安全网终结方法,就要认真考虑清楚,这种额外的保护是否值得你付出这份额外的代价。
+
+显示终止方法模式的示例中所示的四个类(`FileInputStream`、`FileOutputStream`、`Timer` 和 `Connection`),都具有终结方法,当它们的终止方法未能被调用的情况下,这些终止方法充当了安全网。
+
+终结方法的第二种合理用途与对象的本地对等体(`native peer`)有关。本地对等体是一个本地对象(`native object`),普通方法通过本地方法(`native method`)委托给一个本地对象。因为本地对等体不是一个普通对象,所以垃圾回收器不会知道它,当它的 Java 对等体回收的时候,它不会被回收。在本地对等体并不拥有关键资源的前提下,终结方法正是执行这项任务最合适的工具。如果本地对等体拥有被及时终止的资源,那么该类就应该拥有一个显式的终止方法,如前所述。终止方法应该完成完成所有必要的工作以便释放关键的资源。终止方法可以是本地方法,或则它也可以调用本地方法。
+
+值得注意的最重要的一点是,“终结方法链(`finalizer chaining`)”并不会被自动执行。如果类(不是 Object)有终结方法,并且子类覆盖了终结方法,子类的终结方法就必须手工调用超类的终结方法。你应该在一个 try 块中终结子类,并在相应的 finally 块中调用超类的终结方法。这样做可以保证:即使子类的终结过程抛出异常,超类的终结方法也会得到执行。反之亦然。代码示例如下。注意这个示例使用了 Override 注解(`@Override`),这是 Java 1.5 发行版本将它增加到 Java 平台中的。你现在可以不管 Override 注解,或者到第 36 条查阅一下它们是什么意思:
+
+```java
+
+// Manual finalizer chaining
+@Override
+protected void finalize() throws Throwable {
+ try {
+ // Finalize subclass state
+ } finally {
+ super.finalize();
+ }
+}
+
+```
+
+如果子类实现者覆盖了超类的终结方法,但是忘了手工调用超类的终结方法(或者有意选择不调用超类的终结方法),那么超类的终结方法将会永远也不会被调用到。要防范这样粗心大意或者恶意的子类是有可能的,代价就是为每个被终结的对象创建了一个附加的对象。不是把终结方法放在要求终结处理的类中,而是把终结方法在一个匿名的类(见第 22 条)中,该匿名类的唯一用途就是终结它的外围实例(`enclosing instance`)。该匿名类的单个实例被称为**终结方法守卫者(`finalizer guardian`)**,外围类的每个实例都会创建这样一个守卫者。外围实例在它的私有实例域中保存这一个对其终结方法守卫者的唯一引用,因此终结方法守卫者与外围实例可以同时启动终结过程。当守卫者被终结的时候,它执行外围实例所期望的终结行为,就好像它的终结方法是外围对象上的一个方法一样:
+
+```java
+
+// Finalizer Guardian idiom
+public class Foo {
+ // Sole purpose of this object is to finalize outer Foo object
+ private final object finalizerGuardian = new Object() {
+ @Override
+ protect void finalize() throws Throwable {
+ ... // Finalize outer Foo object
+ }
+ };
+
+ ... // Remainder omitted
+}
+
+```
+
+注意,共有类 Foo 并没有终结方法(除了它从 Object 中继承了一个无关紧要的之外),所以子类的终结方法是否调用 super.finalize() 并不重要。对于每一个带有终结方法的非 final 共有类,都应该考虑使用这种方法。
+
+总之,除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。在很少见的情况下,既然使用了终结方法,就要记住调用 super.finalize。如果用终结方法作为安全网,要记得记录终结方法的非法用法。最后,如果需要把终结方法与共有的非 final 类关联起来,请考虑使用终结方法守卫者,以确保即使子类的终结方法未能调用 super.finalize,该终结方法也会被执行。
+
+## 第三章 对于所有对象都通用的方法
+
+### 第 8 条:覆盖 equals 时请遵守通用约定
+
+覆盖 equals 方法看起来很简单,但是有很多覆盖方式会导致错误,并且后果非常严重。最容易避免这类问题的方法就是不覆盖 equals 方法,在这种情况下,类的每个实例都只能与它自身相等。如果满足了一下任何一个条件,这就正是所期望的结果:
+
+- **类的每个实例本质上都是唯一的**。对于代表活动实体而不是值(`value`)的类来说确实如此,例如 Thread。Object 提供的 equals 实现对这些类来说正是正确的行为。
+
+- **不关心类是否提供了“逻辑相等(`logical equality`)”的测试功能**。例如,java.util.Random 覆盖了 equals,以检查两个 Random 实例产生相同的随机数序列,但是设计者并不认为客户需要或者期望这样的功能。在这样的情况下,从 Object 继承得到的 equals 实现已经足够了。
+
+- **超类已经覆盖了 equals,从超类继承过来的行为对于子类也是合适的**。例如,大多数 Set 实现都从 AbstractSet 继承 equals 实现,List 实现从 AbstractList 继承 equals 实现,Map 实现从 AbstractMap 继承实现。
+
+- **类是私有的或是包级私有的,可以确定它的 equals 方法永远不会被调用**。在这种情况下,无疑是应该覆盖 equals 方法的,以防它被意外调用:
+
+```java
+
+@Override
+public boolean equals(Object o) {
+ throw new AssertionError(); // Method is never called
+}
+
+```
+
+那么什么时候应该覆盖 Object.equals 呢?如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖 equals 以实现期望的行为,这是我们就需要覆盖 equals 方法。这通常属于“值类(`value class`)”的情形。值类仅仅是一个表示值的类,例如 Integer 或者 Date。
+
+有一种“值类”不需要覆盖 equals 方法,即用实例受控(见第 1 条)确保“每个值至多只存在一个对象”的类。枚举类型(见第 30 条)就属于这种类。对于这样的类而言,逻辑相同与对象等同是一回事,因此 Object 上的 equals 方法等同于逻辑意义上的 equals 方法。
+
+在覆盖 equals 方法的时候,你必须遵守它的通用约定。下面的内容来自于 Object 的规范[JavaSE6]:
+equals 方法实现了等价关系(`equivalence relation`):
+
+- **自反性(`reflexive`)**。对于任何非 null 的引用值 x,x.equals(x) 必须返回 true。
+
+- **对称性(`symmetric`)**。对于任何非 null 的引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 必须返回 true。
+
+- **传递性(`transitive`)**。对于任何非 null 的引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 也返回 true,那么 x.equals(z) 也必须返回 true。
+
+- **一致性(`consistent`)**。对于任何非 null 的引用值 x 和 y,只要 equals 的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 就会一致地返回 true,或者一致地返回 false。
+
+- **非空性**。对于任何非 null 的引用值 x,x.equlas(null) 必须返回 false。
+
+现在我们按照顺序逐一查看以下 5 个要求:
+
+**自反性(`reflexive`)** --- 第一个要求仅仅说明对象必须等于其本身。很难想象会无意识地违反这一条。假如违背了这一条,然后把该类的实例添加到集合(`collection`)中,该集合的 contains 方法将会果断地告诉你,该集合不包含你刚刚添加的实例。
+
+**对称性(`symmetry`)** --- 第二个要求是说,任何对象对于“它们是否相等”的问题都必须保存一致。与第一个要求不同,若无意中违反这一条,这种情形倒是不难想象。例如,考虑下面的类,它实现了一个区分大小写的字符串。字符串由 toString 保存,但在比较操作中被忽略。
+
+```java
+
+// Broken - violates symmetry
+public final class CaseInsensitiveString {
+ private final String s;
+
+ public CaseInsensitiveString(String s) {
+ if (s == null) {
+ throw new NullPointerException();
+ }
+ this.s = s;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof CaseInsensitiveString) {
+ return s.equalsIgnoreCase(((CaseInsensitiveString)o).s)
+ }
+
+ // One-way interoperability
+ if (o instanceof String) {
+ return s.equalsIgnoreCase((String)o);
+ }
+
+ ...// Remainder ommited
+ }
+}
+
+```
+
+在这个类中,equals 方法的意图非常好,它企图与普通的字符串(String)对象进行互操作。假设我们有一个不区分大小写的字符串和一个普通的字符串:
+
+```java
+
+CaseInsensitiveString cis = new CaseInsensitiveString("TommyYang");
+String s = "tommyyang";
+
+```
+
+如我们所想,cis.equals(s) 返回 true。问题在于 CaseInsensitiveString 类中的 equals 方法知道普通的字符(String)对象,而 String 类中的 equals 方法却不知道不区分大小写的字符串。因此,s.equals(cis)返回 false,显然这违反了对称性。假设你把不区分大小写的字符串对象放到一个集合中:
+
+```java
+
+List cisList = new ArrayList<>();
+cisList.add(cis);
+
+cisList.contains(s);
+
+```
+
+此时 cisList.contains(s) 会返回什么样的结果?没人知道。在 Sun 的当前实现中,它返回 false,但是这只是这个特定实现得出的结果而已。在其它的实现中,它可能返回 true,或抛出运行时(`Runtime`)异常。**一旦违反了 euqals 约定,当其它对象面对你的对象时,你完成不知道这些对象的行为会怎么样**。
+
+为了解决这个问题,你只需要将其与 String 对象互操作的代码移除就可以了。
+
+```java
+
+@Override
+public boolean equals(Object o) {
+ return (o instanceof CaseInsensitiveString)
+ && s.equalsIgnoreCase(((CaseInsensitiveString)o).s);
+}
+
+```
+
+**传递性(`transitive`)** --- euqals 约定的第三个要求是,如果一个对象等于第二个对象,第二个对象等于第三个对象,那个第一个对象一定等于第三个对象。同样地,无意识地违反这条规定的情形也不难想象。考虑子类的情形,它将一个新的值组件(`value component`)添加到超类中。换句话说,子类增加的信息会影响到 equals 的比较结果。我们首先以一个简单的不可变的二维整形 Point 类作为开始:
+
+```java
+
+public class Point {
+ private final int x;
+ private final int y;
+
+ public Point(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Point)) {
+ return false;
+ }
+
+ Point p = (Point)o;
+ return p.x == this.x && p.y == this.y;
+ }
+
+ ...// Remainder ommited
+}
+
+```
+
+假设你想要扩展这个类,为一个点添加颜色信息:
+
+```java
+
+public class ColorPoint extends Point {
+ private final Color color;
+
+ public ColorPoint(int x, int y, Color color) {
+ super(x, y);
+ this.color = color;
+ }
+
+ ...// Remainder ommited
+}
+
+```
+
+equals 方法会怎么样呢?如果完全不提供 equals 方法,而是直接从 Point 类继承过来,在 equals 方法做比较的时候颜色信息就会被忽略掉。虽然这么做不会违反 equals 约定,但是很明显这是无法接受的。那么我们应该怎么重写 equals 方法呢?
+
+```java
+
+// Broken - violates symmetry
+@Override
+public boolean equals(Object o) {
+ if (!(o instanceof ColorPoint)) {
+ return false;
+ }
+
+ return super.equals(o) && ((ColorPoint)o).color == this.color;
+}
+
+```
+
+这个方法的问题在于,你在比较普通点和有色点,以及相反的情形时,可能会得到不同的结果。前一种比较忽略了颜色信息,而后一种比较则总是返回 false,因为参数的类型不正确。为了直观地说明问题所在,我们创建一个普通点和一个有色点:
+
+```java
+
+Point p = new Point(1, 2);
+ColorPoint cp = new ColorPoint(1, 2, Color.Red);
+
+```
+
+然而,p.equals(cp) 返回 true, cp.equals(p) 返回 false。你可以做这样的尝试来修正这个问题,让 ColorPoint.equals 在进行“混合比较”的时候忽略颜色信息:
+
+```java
+
+// Broken - violates transitivity
+@Override
+public boolean equals(Object o) {
+ if (!(o instanceof Point)) {
+ return false;
+ }
+
+ // if o is a normal Point, do a color-blind comparison
+ if (!o instanceof ColorPoint) {
+ return o.equals(this);
+ }
+
+ // o is a ColorPoint, do a full comparison
+ return super.equals(o) && ((ColorPoint)o).color == this.color;
+}
+
+```
+
+这种做法确实提供了对称性,却忽略了传递性:
+
+```java
+
+ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
+Point p2 = new Point(1, 2);
+ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
+
+```
+
+此时 p1.equals(p2) 和 p2.equals(p3) 都返回 true,但 p1.equals(p3) 返回 false,很显然违反了传递性。前两者的比较不考虑颜色信息(“色盲”),而第三者的比较则考虑了颜色信息。
+
+那么,怎么解决上述问题呢?事实上,这是面向对象语言中关于等价关系的一个基本问题。我们**无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留 equals 约定**,除非愿意放弃面向对象的抽象带来的优势。
+
+也许你了解到,在 equals 方法中用 getClass 测试代替 instanceof 测试,可以扩展可实例化的类和增加新的值组件,同时保留 equals 约定:
+
+```java
+
+// Broken - violates Liskov substitution principle
+@Override
+public boolean equals(Object o) {
+ if (o == null || o.getClass() != this.getClass()) {
+ return false;
+ }
+
+ Point p = (Point)o;
+ return p.x == this.x && p.y == this.y;
+}
+
+```
+
+这段程序只有当对象具有相同实现时,才能是对象等同。虽然这样也不算太糟糕,但是结果确实无法接受的。
+
+假设我们编写一个方法,已检测某个整值点是否处在单位圆中。下面是可以采用的一种方法:
+
+```java
+
+// Initialize UnitCircle to contain all Points on the unit circle
+private static final Set unitCircle;
+static {
+ unitCircle = new HashSet<>();
+ unitCircle.add(new Point(1, 0));
+ unitCircle.add(new Point(0, 1));
+ unitCircle.add(new Point(-1, 0));
+ unitCircle.add(new Point(0, -1));
+}
+
+public static boolean onUnitCircle(Point p) {
+ return unitCircle.contains(p);
+}
+
+```
+
+虽然这种这可能不是实现这种功能的最快方式,不过它的效果很好。但是,假设你通过某种不添加值组件的方式扩展了 Point,例如让它的构造器记录创建了多少个实例:
+
+```java
+
+public class CounterPoint extends Point {
+ private static final AtomicInteger counter = new AtomicInteger();
+
+ public CounterPoint(int x, int y) {
+ super(x, y);
+ counter.incrementAndGet();
+ }
+
+ public int numberCreated() {
+ return counter.get();
+ }
+}
+
+```
+
+**里氏替换原则(`Liskov substitution principle`)**认为,一个类型的任何重要属性也将适用于它的子类型,因此为该类型编写的任何方法,在它的子类型上,也应该运行的很好[Liskov87]。但是假设我们将 CounterPointer 实例传递给了 onUnitCircle 方法。如果 Point 类使用了基于 getClass 的 equals 方法,无论 CounterPoint 实例的 x 和 y 的值是多少,onUnitCircle 方法都会返回 false。这时候基于 instanceof 的 equals 方法就会运行的很好。
+
+虽然没有一种令人满意的方法可以既扩展不可实例化的类,又增加值组件,但是还是有一种不错的权宜之计(workaround)。根据第 16 条的建议:复合优先于继承。我们不再让 ColorPoint 继承 Point,而是在 ColorPoint 中加入一个私有的 Point 域,以及一个共有的视图(view)方法(见第 5 条),此方法返回一个与该有色点处在相同位置的普通 Point 对象:
+
+```java
+
+// Add a value component without violating the equals contract
+public class ColorPoint {
+ private final Point point;
+ private final Color color;
+
+ public ColorPoint(int x, int y, Color color) {
+ if (color == null) {
+ throw new NullPointerException();
+ }
+
+ this.point = new Point(x, y);
+ this.color = color;
+ }
+
+ // return the point-view of this color point.
+ public Point asPoint() {
+ return this.point;
+ }
+
+ @Override
+ public boolean equals(Object o){
+ if (!(o instanceof ColorPoint)){
+ return false;
+ }
+
+ ColorPoint cp = (ColorPoint)o;
+
+ return cp.point.equals(this.point) && cp.color.equals(this.color);
+ }
+
+}
+
+```
+
+需要我们记住的是:**复合优先于继承**。
+
+**一致性(`consistency`)** --- equals 约定的第四个条件是,如果两个对象相等,它们就必须始终保持相等,除非它们中有一个对象(或两个都)被修改了。换句话说,可变对象在不同的时候可以与不同的对象相等,而不可变对象则不会这样。当你在写一个类的时候,应该仔细考虑它是否应该不可变的(见第 15 条)。如果类是不可变的,就必须保证 equals 方法满足这样的限制条件:相等的对象永远相等,不相等的对象永远不相等。
+
+无论是否是不可变的,**都不要使 equals 方法依赖于不可靠的资源**。如果违反了这条禁令,要想满足一致性的要求就十分困难了。例如,java.net.URL 的 equals 方法依赖于对 URL 中主机 IP 地址的比较。将一个主机名转变成 IP 地址可能需要访问网络,随着时间的推移,不确保会产生相同的结果。这样会导致 URL 的 equals 方法违反 equals 约定,在实践中有可能引发一些问题。(遗憾的是,因为兼容性的要求,这一行为无法被改变。)除了极少数的例外情况,equals 方法都应该对驻留在内存中的对象执行确定性的计算。
+
+**非空性(`Non-nullity`)** ---最后一个要求是所有的对象都必须不等于 null。为了满足 equals 方法的这个要求,有人会使用通过一个显示的 null 测试来防止这种情况:
+
+```java
+
+@Override
+public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ ...
+}
+
+```
+
+这项测试是不需要的。为了测试其参数的等同性,equals 方法必须把参数转换成适当的类型,以便可以调用它的访问方法(`accessor`),或者访问它的域。在转换之前,equals 方法必须使用 instanceof 操作符,检查其参数是否为正确的类型:
+
+```java
+
+@Override
+public boolean equals(Object o) {
+ if (!(o instanceof MyType)) {
+ return false;
+ }
+ MyType mt = MyType(o);
+ ...
+}
+
+```
+
+如果漏掉了这一步的类型检查,并且传递给 equals 方法的参数又是错误的类型,那么 equals 方法将会抛出 ClassCastException 异常,这就违反了 equals 的约定。由于 instanceof 的特性[JLS, 15.20.2],第一个操作数为 null,不管第二个操作数是不是 null,都会返回 false。所以这样就不需要我们再自己手动地判断是否为空。
+
+结合所有这些方法,得出了以下实现高质量 equals 方法的诀窍:
+
+1. **使用 == 操作符检查“参数是否为这个对象的引用”**。如果是,则返回 true。这只不过是一种性能优化,如果比较操作有可能很昂贵,就值得这么做。
+
+2. **使用 instanceof 操作符“参数是否为正确的类型”**。
+
+3. **把参数转换成正确的类型**。因为转换之前进行过 instanceof 测试,所以确保会成功。
+
+4. **对于该类中的每个“关键”域,检查参数中的域与该对象中对应的域相匹配**。
+
+5. **当你编写完成了 equals 方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?**。
+
+- **覆盖 equals 时总是要覆盖 hashCode(见第 9 条)**。
+- **不要企图让 equals 方法过于智能**。
+- **不要将 equals 声明中的 Object 对象替换为其他的类型**。
+
+### 第 9 条:覆盖 equals 总要覆盖 hashCode
+
+一个很常见的错误根源在于没有覆盖 hashCode 方法。在每个覆盖 equals 方法的类中,也必须覆盖 hashCode 方法。如果不这样做的话,就会违反 Object.hashCode 的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合类包括 HashMap、HashSet 和 HashTable。
+
+下面是约定的内容,摘自 Object 规范[JavaSE6]:
+
+- 在应用程序执行期间,只要对象的 equals 方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode 方法都必须始终如意的返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
+
+- 如果两个对象根据 equals(Object) 方法比较是相等的,那么调用两个对象中任意一个对象的 hashCode 方法都必须产生同样的整数结果。
+
+- 如果两个对象根据 equals(Object) 方法比较是不相等的,那么调用两个对象中任意一个对象的 hashCode 方法,则不一定要产生不同的整数结果。但是作为程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表(hashTable)的性能。
+
+**因没有覆盖 hashCode 而违反的关键约定是第二条:相等的对象必须具有相等的散列码(hash code)**。根据类的 equals 方法,两个截然不同的实例在逻辑上是有可能相等的,但是,根据 Object 类的 hashCode 方法,它们仅仅是两个没有共同之处的对象。因此,对象的 hashCode 方法返回两个看起来是随机的整数,而不是根据第二个约定所要求的那样,返回两个相等的整数。
+
+上 demo,看下面的 PhoneNumber 类,它的 equals 方法是根据第 8 条中给出的“诀窍”构造出来的:
+
+```java
+
+public final class PhoneNumber {
+ private final short areaCode;
+ private final short prefix;
+ private final short lineNumber;
+
+ public PhoneNumber(short areaCode, short prefix, short lineNumber) {
+ this.areaCode = areaCode;
+ this.prefix = prefix;
+ this.lineNumber = lineNumber;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (! (o instanceof PhoneNumber)) {
+ return false;
+ }
+
+ PhoneNumber pn = (PhoneNumber)o;
+
+ return pn.areaCode = this.areaCode && pn.prefix = this.prefix
+ && pn.lineNumber = this.lineNumber;
+ }
+
+ // Broken -- no hashCode method
+
+ ... // Remainder omitted
+}
+
+```
+
+假设你企图将这个类与 HashMap 一起使用:
+
+```java
+
+Map map = new HashMap();
+map.put(new PhoneNumber(21, 210, 20000), "tommy");
+
+```
+
+这时候,你期望的是 map.get(new PhoneNumber(21, 210, 20000)) 会返回 "tommy",但它实际上返回的是 null。注意,
+这里涉及两个实例:第一个被用于插入到 HashMap 中,第二个实例和第一个相等,被作为用于获取数据的 key。由于 PhoneNumber
+没有覆盖 hashCode 方法,从而导致两个相等的实例具有不相等的散列码,违反了 hashCode 的约定。因此,put 方法把 PhoneNumber
+对象存放到一个散列桶(hash bucket)中,get 方法却在另一个散列桶中查找这个 PhoneNumber 对象。即使这两个实例正好被放到
+同一个散列桶里,get 方法也必定会返回 null,因为 HashMap 有一项优化,可以将与每个项相关联的散列码缓存起来,如果散列码不
+匹配,也不必检验对象的等同性。
+
+下面说说如何设计一个好的散列函数,好的散列函数通常倾向于“为不相等的对象产生不同的散列码”。这正是 hashCode 约定中第三条
+的含义。理想情况下,散列函数应该把集合中不相等的实例均匀的分布到所有可能的散列值上。要想完全达到这种理想的情形是非常困难的。
+幸运的是,相对接近这种理想情形并不太困难。下面给出一种简单的解决办法:
+
+1. 把某个非零的常数值,比如说 17,保存在一个名为 res 的 int 类型变量中。
+
+2. 对于对象中每个关键域 f (指 equals 方法中涉及的每个域),完成以下步骤:
+
+ a. 为该域计算 int 类型的散列码 c:
+
+ i. 如果该域是 boolean 类型,则计算(f ? 1 : 0)。
+
+ ii. 如果该域是 byte、char、short 或者 int 类型,则计算 (int)f。
+
+ iii. 如果该域是 long 类型,则计算 (int)(f ^ (f >>> 32))。
+
+ iv. 如果该域是 float 类型,则计算 Float.floatToIntBits(f)。
+
+ v. 如果该域是 double 类型,则计算 Double.floatToLongBits(f),然后按步骤 2.a.iii,
+ 为得到的 long 类型值计算散列值。
+
+ vi. 如果该域是一个对象引用,如果该类的 equals 方法通过递归地调用 equals 的方式来比较这个域,则同样为这个
+ 域递归地调用 hashCode。如果需要更复杂的比较,则为这个域计算一个“范式(canonical representation)”,
+ 然后针对这个范式调用 hashCode。如果这个域的值为 null,则返回 0(或者其它某个常数,但通常是0)。
+
+ vii. 如果该域是一个数组,则要把每个元素当作单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算
+ 一个散列码,然后根据步骤 2.b 中做法把这些散列值组合起来。如果数组域中的每个元素都很重要,可以利用发行版
+ 本 1.5 中增加的其中一个 Arrays.hashCode 方法。
+
+ b. 按照以下公式,把步骤 2.a 中计算得到的散列码 c 合并到 res 中:
+
+ res = res * 31 + c;
+
+3. 返回 res。
+
+4. 写完 hashCode 方法之后,问问自己“相等的实例是否都具有相同的散列码”。要编写单元测试来验证你的推断。如果相等的实例有着
+ 不相同的散列码,则要找出原因,并修正错误。
+
+在散列码的计算过程中,可以把冗余域(`redundant field`)排除在外。换句话说,如果一个域的值可以根据参与计算的其它域值计算
+出来,则可以把这样的域排除在外。必须排除 equals 比较计算中没有用到的任何域,否则很有可能违反 hashCode 约定的第二条。
+
+上述步骤 1 中用到了一个非零的初始值,因此步骤 2.a 中计算的散列值为 0 的那些初始域,会影响到散列值。如果步骤 1 中的初始值
+为 0,则整个散列值将不受这些初始域的影响,因为这些初始域会增加冲突的可能性。值 17 则是任选的。
+
+步骤 2.b 中的乘法部分使得散列值依赖于域的顺序,如果一个类包含多个相似的域,这样的乘法运算就会产生一个更好的散列函数。例如,
+如果 String 散列函数省略了这个乘法部分,那么只是字母顺序不同的所有字符串都会有相同的散列码。之所以选择 31,是因为它是一个
+奇素数。如果乘数是偶数,并且乘法溢出的话,信息就会丢失。因为与 2 相乘等价于移位运算,使用素数的好处并不明显,但是习惯上都是
+使用素数来计算散列结果。31 有个很好的特性,即用移位和减法来替代乘法,可以得到更好的性能:31 \* i == (i << 5) - 1。现代
+JVM 可以自动完成这种优化。
+
+用上述方法,我们重写 hashCode 方法:
+
+```java
+
+@Override
+public int hashCode() {
+ int res = 17;
+ res = 31 * res + areaCode;
+ res = 31 * res + prefix;
+ res = 31 * res + lineNumber;
+ return res;
+}
+
+```
+
+实际上,对于 PhoneNumber 类的 hashCode 实现而言,上面这个方法是非常合理的,相当于 JDK 中的实现。它的做法非常简单,也相当
+快捷,恰当地把不相等的电话号码分散到不同的散列桶中。
+
+如果一个类是不可变的,并且计算散列码的开销的也比较大,就应该考虑把散列码缓存在对象内部,而不是每次请求的时候都重新计算散列码。
+如果你觉得这种类型的大多数对象会被用做散列键(hash keys),就应该在创建实例的时候计算散列码。否则,可以选择 “延迟初始化(`lazily initialize`)” 散列码,一直到 hashCode 第一次被调用的时候才初始化(见 71 条)。如果是 PhoneNumber 会被
+经常用来作为 hash key 的话,那么应该这样实现:
+
+```java
+
+// Lazily initialized, cached hashCode
+private volatile int hashCode;
+
+@Override
+public int hashCode() {
+ int res = hashCode;
+ if (res == 0) {
+ int res = 17;
+ res = 31 * res + areaCode;
+ res = 31 * res + prefix;
+ res = 31 * res + lineNumber;
+ }
+
+ return res;
+}
+
+```
+
+**不要试图从散列码计算中排除掉一个对象的关键部分来提高性能**。虽然这样得到的散列函数运行起来可能会更快,但是它的效果不见得会更好,可能会导致散列表慢到根本无法使用。
+
+Java 平台类库中的许多类,比如 String、 Integer 和 Date,都可以把它们的 hashCode 方法返回的确切值规定为该实例值的一个函数。一般来说,这并不是一个好主意,因为这样做严格地限制了在将来的版本中改进散列函数的能力。如果没有规定散列函数的细节,那么当你
+发现了它的内部缺陷时,就可以在后面的发行版本中修正它,确信没有任何客户端依赖于散列函数返回的确切值。
+
+### 第 10 条:始终覆盖 toString
+
+虽然 java.lang.Object 提供了 toString 方法的一个实现,但它返回的字符串通常并不是类的用户所期望看到的。它包含类的名称,以及一个 “@” 符号,接着是散列码的无符号十六进制表示法,例如 “PhoneNumber@193b2d”。toString 的通用约定指出,被返回的字符串应该是一个“简洁的,但信息丰富,并易于阅读的表达形式”[JavaSE 6]。尽管有人认为“PhoneNumber@193b2d”,但是与“(021)589-5588”比起来,它还算不上是信息丰富的。toString 的约定进一步指出,“建议所有的子类都覆盖这个方法”。这是一个非常好的建议,真的!因为这样我们可以按照自己想要的形式去打印一个对象,比如 `System.out.println(phoneNumber)`,默认就会打印出 phoneNumber.toString()。这也就是建议所有子类都重写 toString 的原因。
+
+虽然遵守 toString 的约定并不像遵守 equals 和 hashCode(见[第 8 条](第 8 条:覆盖 equals 时请遵守通用约定) 和 [第 9 条](第 9 条:覆盖 equals 总要覆盖 hashCode))的约定那么重要,但是,提供好的 toString 可以使类用起来更加的舒适。当对象被传递给 println、printf、字符串连接操作符(+)以及 assert 或者被调试器打印出来时,toString 方法会被自动调用。
+
+如果为 PhoneNumber 提供了好的 toString 方法,那么,要产生有用的诊断信息会非常简单:
+
+```java
+
+System.out.println("failed to connect: " + phoneNumber)
+
+```
+
+不管是否覆盖了 toString 方法,程序员都将以这种方式来产生诊断信息,但是,如果没有覆盖 toString 方法,产生的消息将难以理解。提供好的 toString 方法,不仅有益于这个类的实例,同样也有益于那些包含这些实例引用的引用的对象,特别是集合对象。打印 Map 时,有下面两条信息:“Tommy = (021)589-5588” 和 “Tommy = PhoneNumber@193b2d”,你更愿意看哪一个?
+
+在实际应用中,toString 方法应该返回对象中包含的所有值得关注的信息,譬如上面的电话号码的例子一样。如果对象太大,或者对象包含的状态信息难以用字符串来表达,这样做就有点不切实际。
+
+在实现 toString 的时候,必须要做出一个重要的决定:是否在文档中指定返回值的格式。对于值类(value class),比如电话号码类、矩阵类,也建议这么做。指定格式的好处是,它可以被用做一种标准的、明确的、适合人阅读的对象表示法。这种表示法可以用于输入和输出,以及用在永久适合人类阅读的数据对象中,例如 XML 文档。如果你指定了格式,那么你最好提供一个相匹配的静态工厂或者构造器,以便于程序员可以很容易地在对象和它的字符串表示法之间来回转换。JDK 类库中的许多值类都采用了这种做法,包括 BigInteger、 BigDecimal 和绝大多数的基本类型包装类(boxed primitive class)。
+
+指定 toString 返回值的格式也有不足之处:如果这个类已经被广泛使用,一旦指定格式,就必须始终如一地坚持这种格式。程序员将会编写出相应的代码来解析这种字符串表示法、产生字符串表示法,以及把字符串表示法嵌入到持久的数据中。如果将来的发行版本中改变了这种表示法,就破坏他们的代码和数据,他们当然会抱怨。如果不指定格式,就可以保留灵活性,便于在将来的发行版本中增加信息,或者改进格式。
+
+**无论你是否决定指定格式,都应该在文档中明确地表明你的意图**。如下,[第 9 条](第 9 条:覆盖 equals 总要覆盖 hashCode)中 PhoneNumber 的 toString 方法:
+
+```java
+
+/**
+ * return (XXX)-YYY-ZZZZ,where XXX is the area code, YYY is the prefix,
+ * and ZZZZ is the line number.
+ */
+@Override
+public String toString() {
+ return String.format("(%03d)-%03d-%04d", this.areaCode, this.prefix, this.lineNumber);
+}
+
+```
+
+如果你决定不指定格式,那么在文档注释部分也应该有如下所示的指示信息:
+
+```java
+
+/**
+ * return a brief description of this potion.
+ */
+@Override
+public String toString() {
+ ...
+}
+
+```
+
+对于那些依赖于格式的细节进行编程或者产生永久数据的程序员,在读到这段注释之后,一旦格式被改变,则只能自己承担后果。
+
+无论是否指定格式,**都为 toString 返回值中包含的所有信息,提供一种编程式的访问途径**。
diff --git a/docs/getting-started.md b/docs/getting-started.md
new file mode 100644
index 0000000..56718b4
--- /dev/null
+++ b/docs/getting-started.md
@@ -0,0 +1,71 @@
+# 互联网公司 IT 技术面试题集
+
+[](https://github.com/doocs/coding-interview/blob/main/LICENSE)
+[](https://github.com/doocs/coding-interview/stargazers)
+[](https://github.com/doocs/coding-interview/issues)
+[](https://github.com/doocs/coding-interview/network/members)
+[](http://makeapullrequest.com)
+
+## 项目介绍
+
+本仓库用于记录各大互联网公司 IT 技术面试高频题以及经典书籍读书笔记,包括《剑指 Offer》、《编程之美》、《代码整洁之道》等,抽空更新中。
+
+## 站点
+
+https://interview.doocs.org
+
+## 书籍笔记
+
+### [《剑指 Offer》](/coding-interview.md)
+
+这本书选取的[题目](/coding-interview.md)都是被各大公司面试官反复采用的编程题,极具实战意义。当然,如果一开始觉得这些题目比较难,也是很正常的,因为大公司的面试本身就不简单。我们逐步掌握书中总结的解题方法之后,再去面试大公司,将会轻松很多。
+
+推荐三个在线刷《剑指 Offer》的平台,个人比较倾向于 [LeetCode 中国](https://leetcode.cn/problemset/lcof/) 。
+
+- [LeetCode 中国](https://leetcode.cn/problemset/lcof/):近期上线,支持多种编程语言,墙裂推荐。
+- [AcWing](https://www.acwing.com/problem/):支持 Java11。团队成员来自北大清华,剑指 Offer 第二版题目都有。
+- [NowCoder](https://www.nowcoder.com/ta/coding-interviews):这个应该大多数人都知道,但是没有剑指 Offer 第二版新增的题目。
+
+### [《代码整洁之道》](/clean-code.md)
+
+这本书名为 _Clean Code_,乃是 Object Mentor(鲍勃大叔开办的技术咨询和培训公司)一干大牛在编程方面的经验累积。写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。
+
+作者 Robert C. Martin 在书中阐述了代码各个方面如何做到整洁的经验与最佳实践。我们若能长期遵照这些经验编写代码,所谓“代码感”也就自然而然滋生出来。
+
+### [《阿里巴巴 Java 开发手册》](/effective-coding.md)
+
+别人都说我们是搬砖的码农,但我们知道自己是追求个性的艺术家。也许我们不会过多在意自己的外表和穿着,但在我们不羁的外表下,骨子里追求着代码的美、系统的美、设计的美,代码规范其实就是一个对程序美的定义。
+
+### [《枕边算法书》](/algorithm-stories.md)
+
+这本书,我是把它当作一本故事书来读的,里面的部分知识点还挺有意思。
+
+### [《Effective Java》](/effective-java.md)
+
+这本书共包含了 78 个条目,每个条目讨论一条规则。它适用于任何具有实际 Java 工作经验的工程师,对于高级工程师,也能够提供一些发人深思的东西,是所有 Java 工程师必读书籍之一。
+
+---
+
+## Doocs 社区优质项目
+
+Doocs 技术社区,致力于打造一个内容完整、持续成长的互联网开发者学习生态圈!以下是 Doocs 旗下的一些优秀项目,欢迎各位开发者朋友持续保持关注。
+
+| # | 项目 | 描述 | 热度 |
+| --- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | [advanced-java](https://github.com/doocs/advanced-java) | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。 |   |
+| 2 | [leetcode](https://github.com/doocs/leetcode) | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解。 |   |
+| 3 | [source-code-hunter](https://github.com/doocs/source-code-hunter) | 互联网常用组件框架源码分析。 |   |
+| 4 | [jvm](https://github.com/doocs/jvm) | Java 虚拟机底层原理知识总结。 |   |
+| 5 | [coding-interview](https://github.com/doocs/coding-interview) | 代码面试题集,包括《剑指 Offer》、《编程之美》等。 |   |
+| 6 | [md](https://github.com/doocs/md) | 一款高度简洁的微信 Markdown 编辑器。 |   |
+| 7 | [technical-books](https://github.com/doocs/technical-books) | 值得一看的技术书籍列表。 |   |
+
+## 贡献者
+
+感谢以下所有朋友对 [Doocs 技术社区](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。
+
+
+
+
+
+
diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png
index 354e418..cbcae6f 100644
Binary files a/docs/images/favicon-16x16.png and b/docs/images/favicon-16x16.png differ
diff --git a/docs/images/favicon-32x32.png b/docs/images/favicon-32x32.png
index 1b1a742..4902f43 100644
Binary files a/docs/images/favicon-32x32.png and b/docs/images/favicon-32x32.png differ
diff --git a/docs/images/github-doocs-yanglbme-collabocats.png b/docs/images/github-doocs-yanglbme-collabocats.png
index ecc2aa3..709dcc5 100644
Binary files a/docs/images/github-doocs-yanglbme-collabocats.png and b/docs/images/github-doocs-yanglbme-collabocats.png differ
diff --git a/docs/images/icon.png b/docs/images/icon.png
index 71de33d..cecc04c 100644
Binary files a/docs/images/icon.png and b/docs/images/icon.png differ
diff --git a/docs/images/island-eye-color.jpg b/docs/images/island-eye-color.jpg
new file mode 100644
index 0000000..8ea8856
Binary files /dev/null and b/docs/images/island-eye-color.jpg differ
diff --git a/docs/images/odd-even.png b/docs/images/odd-even.png
index d9f017b..b82dad9 100644
Binary files a/docs/images/odd-even.png and b/docs/images/odd-even.png differ
diff --git a/docs/images/random-list-step1.png b/docs/images/random-list-step1.png
index 701e37f..60eb5e5 100644
Binary files a/docs/images/random-list-step1.png and b/docs/images/random-list-step1.png differ
diff --git a/docs/images/random-list-step2.png b/docs/images/random-list-step2.png
index 3316254..e48a9a4 100644
Binary files a/docs/images/random-list-step2.png and b/docs/images/random-list-step2.png differ
diff --git a/docs/images/random-list-step3.png b/docs/images/random-list-step3.png
index a8e36ee..4afc3e3 100644
Binary files a/docs/images/random-list-step3.png and b/docs/images/random-list-step3.png differ
diff --git a/docs/images/random-list.png b/docs/images/random-list.png
index 4b9b4b4..f858c6c 100644
Binary files a/docs/images/random-list.png and b/docs/images/random-list.png differ
diff --git a/docs/images/water-cup.jpg b/docs/images/water-cup.jpg
new file mode 100644
index 0000000..67a35df
Binary files /dev/null and b/docs/images/water-cup.jpg differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..d376f65
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,47 @@
+---
+# https://vitepress.dev/reference/default-theme-home-page
+layout: home
+
+hero:
+ name: "coding-interview"
+ text: "互联网公司 IT 技术面试题集"
+ tagline: Doocs 技术社区出品
+ actions:
+ - theme: brand
+ text: 开始阅读
+ link: /getting-started
+ - theme: alt
+ text: GitHub
+ link: https://github.com/doocs/coding-interview
+
+features:
+ - icon: 📚
+ title: 剑指 Offer
+ details: 经典面试题解析合集,涵盖算法、数据结构与解题思路。
+ link: /coding-interview
+
+ - icon: 💡
+ title: 编程之美
+ details: 微软出品,充满趣味与思考的编程挑战题解。
+ link: /the-beauty-of-programming
+
+ - icon: 📝
+ title: 代码整洁之道
+ details: 如何写出可读、可维护的高质量代码,编码风格提升宝典。
+ link: /clean-code
+
+ - icon: 🎓
+ title: 阿里巴巴 Java 开发手册
+ details: 阿里内部实践沉淀的 Java 编程规范手册。
+ link: /effective-coding
+
+ - icon: 🛏️
+ title: 枕边算法书
+ details: 轻松入门算法的最佳伴侣,睡前读物般的温柔讲解。
+ link: /algorithm-stories
+
+ - icon: ☕
+ title: Effective Java
+ details: Java 编程进阶指南,覆盖最佳实践和设计模式。
+ link: /effective-java
+---
diff --git a/docs/public/favicon-16x16.png b/docs/public/favicon-16x16.png
new file mode 100644
index 0000000..0c40121
Binary files /dev/null and b/docs/public/favicon-16x16.png differ
diff --git a/docs/public/favicon-32x32.png b/docs/public/favicon-32x32.png
new file mode 100644
index 0000000..c95f65c
Binary files /dev/null and b/docs/public/favicon-32x32.png differ
diff --git a/docs/the-beauty-of-programming.md b/docs/the-beauty-of-programming.md
index 391c553..2acb12b 100644
--- a/docs/the-beauty-of-programming.md
+++ b/docs/the-beauty-of-programming.md
@@ -3,12 +3,15 @@
## 1.2 中国象棋将帅问题
### 题目描述
+
中国象棋里,“将”、“帅”不能照面。现假设棋盘上只有“将” `A`和“帅” `B`二子。
A、B 二子被限制在己方 `3*3` 的格子里运动。请写处一个程序,输出 A、B 所有合法位置。要求在代码中**只能使用一个变量**。
### 解法
+
程序的大致框架如下:
+
```
遍历 A 的位置
遍历 B 的 位置
@@ -18,6 +21,7 @@ A、B 二子被限制在己方 `3*3` 的格子里运动。请写处一个程序
本题的难点在于如何只用一个变量来实现。
对于本题,每个子只需要 9 个数字就可以表达它的全部位置。
+
```
1 - 2 - 3
| | |
@@ -27,6 +31,7 @@ A、B 二子被限制在己方 `3*3` 的格子里运动。请写处一个程序
```
#### 解法一
+
一个 8 位的 byte 类型能够表达 `2^8=256` 个值,所以可以用前 4 个 bit 表示 A 的位置,用后面的 4 bit 表示 B 的位置。
```java
@@ -77,10 +82,11 @@ public class Solution {
}
}
}
-
+
```
#### 解法三
+
这是 C 语言的实现的另一个解法。
```c
@@ -99,5 +105,5 @@ int main() {
}
}
}
-}
-```
\ No newline at end of file
+}
+```
diff --git a/images/favicon-16x16.png b/images/favicon-16x16.png
deleted file mode 100644
index 354e418..0000000
Binary files a/images/favicon-16x16.png and /dev/null differ
diff --git a/images/favicon-32x32.png b/images/favicon-32x32.png
deleted file mode 100644
index 1b1a742..0000000
Binary files a/images/favicon-32x32.png and /dev/null differ
diff --git a/images/github-doocs-yanglbme-collabocats.png b/images/github-doocs-yanglbme-collabocats.png
deleted file mode 100644
index ecc2aa3..0000000
Binary files a/images/github-doocs-yanglbme-collabocats.png and /dev/null differ
diff --git a/images/icon.png b/images/icon.png
deleted file mode 100644
index 71de33d..0000000
Binary files a/images/icon.png and /dev/null differ
diff --git a/images/odd-even.png b/images/odd-even.png
deleted file mode 100644
index d9f017b..0000000
Binary files a/images/odd-even.png and /dev/null differ
diff --git a/images/random-list-step1.png b/images/random-list-step1.png
deleted file mode 100644
index 701e37f..0000000
Binary files a/images/random-list-step1.png and /dev/null differ
diff --git a/images/random-list-step2.png b/images/random-list-step2.png
deleted file mode 100644
index 3316254..0000000
Binary files a/images/random-list-step2.png and /dev/null differ
diff --git a/images/random-list-step3.png b/images/random-list-step3.png
deleted file mode 100644
index a8e36ee..0000000
Binary files a/images/random-list-step3.png and /dev/null differ
diff --git a/images/random-list.png b/images/random-list.png
deleted file mode 100644
index 4b9b4b4..0000000
Binary files a/images/random-list.png and /dev/null differ
diff --git a/index.html b/index.html
deleted file mode 100644
index f069a87..0000000
--- a/index.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
- 代码面试题集
-
-
-
-
-
-
-
-
- Welcome to Coding-Interview
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7333a01
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "devDependencies": {
+ "vitepress": "^2.0.0-alpha.17"
+ },
+ "scripts": {
+ "docs:dev": "vitepress dev docs",
+ "docs:build": "vitepress build docs",
+ "docs:preview": "vitepress preview docs"
+ },
+ "dependencies": {
+ "vitepress-plugin-comment-with-giscus": "^1.1.15"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..73ed99c
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1460 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ vitepress-plugin-comment-with-giscus:
+ specifier: ^1.1.15
+ version: 1.1.15(vue@3.5.32)
+ devDependencies:
+ vitepress:
+ specifier: ^2.0.0-alpha.17
+ version: 2.0.0-alpha.17(postcss@8.5.10)
+
+packages:
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.2':
+ resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
+ '@docsearch/css@4.6.2':
+ resolution: {integrity: sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==}
+
+ '@docsearch/js@4.6.2':
+ resolution: {integrity: sha512-qj1yoxl3y4GKoK7+VM6fq/rQqPnvUmg3IKzJ9x0VzN14QVzdB/SG/J6VfV1BWT5RcPUFxIcVwoY1fwHM2fSRRw==}
+
+ '@docsearch/sidepanel-js@4.6.2':
+ resolution: {integrity: sha512-Pni85AP/GwRj7fFg8cBJp0U04tzbueBvWSd3gysgnOsVnQVSZwSYncfErUScLE1CAtR+qocPDFjmYR9AMRNJtQ==}
+
+ '@esbuild/aix-ppc64@0.27.7':
+ resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.7':
+ resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.7':
+ resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.7':
+ resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.7':
+ resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.7':
+ resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.7':
+ resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.7':
+ resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.7':
+ resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.7':
+ resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.7':
+ resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.7':
+ resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.7':
+ resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.7':
+ resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.7':
+ resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.7':
+ resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.7':
+ resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.7':
+ resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.7':
+ resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.7':
+ resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.7':
+ resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.7':
+ resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@giscus/vue@2.4.0':
+ resolution: {integrity: sha512-QOxKHgsMT91myyQagP2v20YYAei1ByZuc3qcaYxbHx4AwOeyVrybDIuRFwG9YDv6OraC86jYnU4Ixd37ddC/0A==}
+ peerDependencies:
+ vue: '>=3.2.0'
+
+ '@iconify-json/simple-icons@1.2.78':
+ resolution: {integrity: sha512-I3lkNp0Qu7q2iZWkdcf/I2hqGhzK6qxdILh9T7XqowQrnpmG/BayDsiCf6PktDoWlW0U971xA5g+panm+NFrfQ==}
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@lit-labs/ssr-dom-shim@1.5.1':
+ resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==}
+
+ '@lit/reactive-element@2.1.2':
+ resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==}
+
+ '@rolldown/pluginutils@1.0.0-rc.13':
+ resolution: {integrity: sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==}
+
+ '@rollup/rollup-android-arm-eabi@4.60.1':
+ resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.60.1':
+ resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.60.1':
+ resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.60.1':
+ resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.60.1':
+ resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.60.1':
+ resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.60.1':
+ resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.60.1':
+ resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.60.1':
+ resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.60.1':
+ resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.60.1':
+ resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.60.1':
+ resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.60.1':
+ resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.60.1':
+ resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.60.1':
+ resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.60.1':
+ resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.60.1':
+ resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.60.1':
+ resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.60.1':
+ resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.60.1':
+ resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.60.1':
+ resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.60.1':
+ resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.60.1':
+ resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.60.1':
+ resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.60.1':
+ resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@shikijs/core@3.23.0':
+ resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==}
+
+ '@shikijs/engine-javascript@3.23.0':
+ resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==}
+
+ '@shikijs/engine-oniguruma@3.23.0':
+ resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==}
+
+ '@shikijs/langs@3.23.0':
+ resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==}
+
+ '@shikijs/themes@3.23.0':
+ resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==}
+
+ '@shikijs/transformers@3.23.0':
+ resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==}
+
+ '@shikijs/types@3.23.0':
+ resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+ '@types/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.2':
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@vitejs/plugin-vue@6.0.6':
+ resolution: {integrity: sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
+ vue: ^3.2.25
+
+ '@vue/compiler-core@3.5.32':
+ resolution: {integrity: sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==}
+
+ '@vue/compiler-dom@3.5.32':
+ resolution: {integrity: sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==}
+
+ '@vue/compiler-sfc@3.5.32':
+ resolution: {integrity: sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==}
+
+ '@vue/compiler-ssr@3.5.32':
+ resolution: {integrity: sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==}
+
+ '@vue/devtools-api@8.1.1':
+ resolution: {integrity: sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==}
+
+ '@vue/devtools-kit@8.1.1':
+ resolution: {integrity: sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==}
+
+ '@vue/devtools-shared@8.1.1':
+ resolution: {integrity: sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==}
+
+ '@vue/reactivity@3.5.32':
+ resolution: {integrity: sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==}
+
+ '@vue/runtime-core@3.5.32':
+ resolution: {integrity: sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==}
+
+ '@vue/runtime-dom@3.5.32':
+ resolution: {integrity: sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==}
+
+ '@vue/server-renderer@3.5.32':
+ resolution: {integrity: sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==}
+ peerDependencies:
+ vue: 3.5.32
+
+ '@vue/shared@3.5.32':
+ resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==}
+
+ '@vueuse/core@14.2.1':
+ resolution: {integrity: sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/integrations@14.2.1':
+ resolution: {integrity: sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA==}
+ peerDependencies:
+ async-validator: ^4
+ axios: ^1
+ change-case: ^5
+ drauu: ^0.4
+ focus-trap: ^7 || ^8
+ fuse.js: ^7
+ idb-keyval: ^6
+ jwt-decode: ^4
+ nprogress: ^0.2
+ qrcode: ^1.5
+ sortablejs: ^1
+ universal-cookie: ^7 || ^8
+ vue: ^3.5.0
+ peerDependenciesMeta:
+ async-validator:
+ optional: true
+ axios:
+ optional: true
+ change-case:
+ optional: true
+ drauu:
+ optional: true
+ focus-trap:
+ optional: true
+ fuse.js:
+ optional: true
+ idb-keyval:
+ optional: true
+ jwt-decode:
+ optional: true
+ nprogress:
+ optional: true
+ qrcode:
+ optional: true
+ sortablejs:
+ optional: true
+ universal-cookie:
+ optional: true
+
+ '@vueuse/metadata@14.2.1':
+ resolution: {integrity: sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==}
+
+ '@vueuse/shared@14.2.1':
+ resolution: {integrity: sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ birpc@2.9.0:
+ resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ entities@7.0.1:
+ resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
+ engines: {node: '>=0.12'}
+
+ esbuild@0.27.7:
+ resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ focus-trap@8.0.1:
+ resolution: {integrity: sha512-9ptSG6z51YQOstI/oN4XuVGP/03u2nh0g//qz7L6zX0i6PZiPnkcf3GenXq7N2hZnASXaMxTPpbKwdI+PFvxlw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ giscus@1.6.0:
+ resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==}
+
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+
+ lit-element@4.2.2:
+ resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==}
+
+ lit-html@3.3.2:
+ resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==}
+
+ lit@3.3.2:
+ resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ mark.js@8.11.1:
+ resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
+
+ mdast-util-to-hast@13.2.1:
+ resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ minisearch@7.2.0:
+ resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ oniguruma-parser@0.12.1:
+ resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
+
+ oniguruma-to-es@4.3.5:
+ resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==}
+
+ perfect-debounce@2.1.0:
+ resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.10:
+ resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ property-information@7.1.0:
+ resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
+
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@6.1.0:
+ resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
+
+ rollup@4.60.1:
+ resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ shiki@3.23.0:
+ resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ tabbable@6.4.0:
+ resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
+
+ tinyglobby@0.2.16:
+ resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
+ engines: {node: '>=12.0.0'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.1.0:
+ resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@7.3.2:
+ resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitepress-plugin-comment-with-giscus@1.1.15:
+ resolution: {integrity: sha512-1DJjgN+7SYvn5ZkjuSXPmz7nlqfcrh4qCGGviiZghA2ELXnaO2m9WY7m+RisPSaqCn90xqe0JbO2T4NMq8iUBg==}
+
+ vitepress@2.0.0-alpha.17:
+ resolution: {integrity: sha512-Z3VPUpwk/bHYqt1uMVOOK1/4xFiWQov1GNc2FvMdz6kvje4JRXEOngVI9C+bi5jeedMSHiA4dwKkff1NCvbZ9Q==}
+ hasBin: true
+ peerDependencies:
+ markdown-it-mathjax3: ^4
+ oxc-minify: '*'
+ postcss: ^8
+ peerDependenciesMeta:
+ markdown-it-mathjax3:
+ optional: true
+ oxc-minify:
+ optional: true
+ postcss:
+ optional: true
+
+ vue@3.5.32:
+ resolution: {integrity: sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/parser@7.29.2':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@docsearch/css@4.6.2': {}
+
+ '@docsearch/js@4.6.2': {}
+
+ '@docsearch/sidepanel-js@4.6.2': {}
+
+ '@esbuild/aix-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm@0.27.7':
+ optional: true
+
+ '@esbuild/android-x64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.7':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.7':
+ optional: true
+
+ '@giscus/vue@2.4.0(vue@3.5.32)':
+ dependencies:
+ giscus: 1.6.0
+ vue: 3.5.32
+
+ '@iconify-json/simple-icons@1.2.78':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify/types@2.0.0': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@lit-labs/ssr-dom-shim@1.5.1': {}
+
+ '@lit/reactive-element@2.1.2':
+ dependencies:
+ '@lit-labs/ssr-dom-shim': 1.5.1
+
+ '@rolldown/pluginutils@1.0.0-rc.13': {}
+
+ '@rollup/rollup-android-arm-eabi@4.60.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.60.1':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.60.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.60.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.60.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.60.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.60.1':
+ optional: true
+
+ '@shikijs/core@3.23.0':
+ dependencies:
+ '@shikijs/types': 3.23.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/engine-javascript@3.23.0':
+ dependencies:
+ '@shikijs/types': 3.23.0
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 4.3.5
+
+ '@shikijs/engine-oniguruma@3.23.0':
+ dependencies:
+ '@shikijs/types': 3.23.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@3.23.0':
+ dependencies:
+ '@shikijs/types': 3.23.0
+
+ '@shikijs/themes@3.23.0':
+ dependencies:
+ '@shikijs/types': 3.23.0
+
+ '@shikijs/transformers@3.23.0':
+ dependencies:
+ '@shikijs/core': 3.23.0
+ '@shikijs/types': 3.23.0
+
+ '@shikijs/types@3.23.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.2':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/mdurl@2.0.0': {}
+
+ '@types/trusted-types@2.0.7': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@vitejs/plugin-vue@6.0.6(vite@7.3.2)(vue@3.5.32)':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-rc.13
+ vite: 7.3.2
+ vue: 3.5.32
+
+ '@vue/compiler-core@3.5.32':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@vue/shared': 3.5.32
+ entities: 7.0.1
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.32':
+ dependencies:
+ '@vue/compiler-core': 3.5.32
+ '@vue/shared': 3.5.32
+
+ '@vue/compiler-sfc@3.5.32':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@vue/compiler-core': 3.5.32
+ '@vue/compiler-dom': 3.5.32
+ '@vue/compiler-ssr': 3.5.32
+ '@vue/shared': 3.5.32
+ estree-walker: 2.0.2
+ magic-string: 0.30.21
+ postcss: 8.5.10
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.32':
+ dependencies:
+ '@vue/compiler-dom': 3.5.32
+ '@vue/shared': 3.5.32
+
+ '@vue/devtools-api@8.1.1':
+ dependencies:
+ '@vue/devtools-kit': 8.1.1
+
+ '@vue/devtools-kit@8.1.1':
+ dependencies:
+ '@vue/devtools-shared': 8.1.1
+ birpc: 2.9.0
+ hookable: 5.5.3
+ perfect-debounce: 2.1.0
+
+ '@vue/devtools-shared@8.1.1': {}
+
+ '@vue/reactivity@3.5.32':
+ dependencies:
+ '@vue/shared': 3.5.32
+
+ '@vue/runtime-core@3.5.32':
+ dependencies:
+ '@vue/reactivity': 3.5.32
+ '@vue/shared': 3.5.32
+
+ '@vue/runtime-dom@3.5.32':
+ dependencies:
+ '@vue/reactivity': 3.5.32
+ '@vue/runtime-core': 3.5.32
+ '@vue/shared': 3.5.32
+ csstype: 3.2.3
+
+ '@vue/server-renderer@3.5.32(vue@3.5.32)':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.32
+ '@vue/shared': 3.5.32
+ vue: 3.5.32
+
+ '@vue/shared@3.5.32': {}
+
+ '@vueuse/core@14.2.1(vue@3.5.32)':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 14.2.1
+ '@vueuse/shared': 14.2.1(vue@3.5.32)
+ vue: 3.5.32
+
+ '@vueuse/integrations@14.2.1(focus-trap@8.0.1)(vue@3.5.32)':
+ dependencies:
+ '@vueuse/core': 14.2.1(vue@3.5.32)
+ '@vueuse/shared': 14.2.1(vue@3.5.32)
+ vue: 3.5.32
+ optionalDependencies:
+ focus-trap: 8.0.1
+
+ '@vueuse/metadata@14.2.1': {}
+
+ '@vueuse/shared@14.2.1(vue@3.5.32)':
+ dependencies:
+ vue: 3.5.32
+
+ birpc@2.9.0: {}
+
+ ccount@2.0.1: {}
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ csstype@3.2.3: {}
+
+ dequal@2.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ entities@7.0.1: {}
+
+ esbuild@0.27.7:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.7
+ '@esbuild/android-arm': 0.27.7
+ '@esbuild/android-arm64': 0.27.7
+ '@esbuild/android-x64': 0.27.7
+ '@esbuild/darwin-arm64': 0.27.7
+ '@esbuild/darwin-x64': 0.27.7
+ '@esbuild/freebsd-arm64': 0.27.7
+ '@esbuild/freebsd-x64': 0.27.7
+ '@esbuild/linux-arm': 0.27.7
+ '@esbuild/linux-arm64': 0.27.7
+ '@esbuild/linux-ia32': 0.27.7
+ '@esbuild/linux-loong64': 0.27.7
+ '@esbuild/linux-mips64el': 0.27.7
+ '@esbuild/linux-ppc64': 0.27.7
+ '@esbuild/linux-riscv64': 0.27.7
+ '@esbuild/linux-s390x': 0.27.7
+ '@esbuild/linux-x64': 0.27.7
+ '@esbuild/netbsd-arm64': 0.27.7
+ '@esbuild/netbsd-x64': 0.27.7
+ '@esbuild/openbsd-arm64': 0.27.7
+ '@esbuild/openbsd-x64': 0.27.7
+ '@esbuild/openharmony-arm64': 0.27.7
+ '@esbuild/sunos-x64': 0.27.7
+ '@esbuild/win32-arm64': 0.27.7
+ '@esbuild/win32-ia32': 0.27.7
+ '@esbuild/win32-x64': 0.27.7
+
+ estree-walker@2.0.2: {}
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ focus-trap@8.0.1:
+ dependencies:
+ tabbable: 6.4.0
+
+ fsevents@2.3.3:
+ optional: true
+
+ giscus@1.6.0:
+ dependencies:
+ lit: 3.3.2
+
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ hookable@5.5.3: {}
+
+ html-void-elements@3.0.0: {}
+
+ lit-element@4.2.2:
+ dependencies:
+ '@lit-labs/ssr-dom-shim': 1.5.1
+ '@lit/reactive-element': 2.1.2
+ lit-html: 3.3.2
+
+ lit-html@3.3.2:
+ dependencies:
+ '@types/trusted-types': 2.0.7
+
+ lit@3.3.2:
+ dependencies:
+ '@lit/reactive-element': 2.1.2
+ lit-element: 4.2.2
+ lit-html: 3.3.2
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ mark.js@8.11.1: {}
+
+ mdast-util-to-hast@13.2.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.3.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ minisearch@7.2.0: {}
+
+ nanoid@3.3.11: {}
+
+ oniguruma-parser@0.12.1: {}
+
+ oniguruma-to-es@4.3.5:
+ dependencies:
+ oniguruma-parser: 0.12.1
+ regex: 6.1.0
+ regex-recursion: 6.0.2
+
+ perfect-debounce@2.1.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.4: {}
+
+ postcss@8.5.10:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ property-information@7.1.0: {}
+
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@6.1.0:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ rollup@4.60.1:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.60.1
+ '@rollup/rollup-android-arm64': 4.60.1
+ '@rollup/rollup-darwin-arm64': 4.60.1
+ '@rollup/rollup-darwin-x64': 4.60.1
+ '@rollup/rollup-freebsd-arm64': 4.60.1
+ '@rollup/rollup-freebsd-x64': 4.60.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.60.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.60.1
+ '@rollup/rollup-linux-arm64-gnu': 4.60.1
+ '@rollup/rollup-linux-arm64-musl': 4.60.1
+ '@rollup/rollup-linux-loong64-gnu': 4.60.1
+ '@rollup/rollup-linux-loong64-musl': 4.60.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.60.1
+ '@rollup/rollup-linux-ppc64-musl': 4.60.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.60.1
+ '@rollup/rollup-linux-riscv64-musl': 4.60.1
+ '@rollup/rollup-linux-s390x-gnu': 4.60.1
+ '@rollup/rollup-linux-x64-gnu': 4.60.1
+ '@rollup/rollup-linux-x64-musl': 4.60.1
+ '@rollup/rollup-openbsd-x64': 4.60.1
+ '@rollup/rollup-openharmony-arm64': 4.60.1
+ '@rollup/rollup-win32-arm64-msvc': 4.60.1
+ '@rollup/rollup-win32-ia32-msvc': 4.60.1
+ '@rollup/rollup-win32-x64-gnu': 4.60.1
+ '@rollup/rollup-win32-x64-msvc': 4.60.1
+ fsevents: 2.3.3
+
+ shiki@3.23.0:
+ dependencies:
+ '@shikijs/core': 3.23.0
+ '@shikijs/engine-javascript': 3.23.0
+ '@shikijs/engine-oniguruma': 3.23.0
+ '@shikijs/langs': 3.23.0
+ '@shikijs/themes': 3.23.0
+ '@shikijs/types': 3.23.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ source-map-js@1.2.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ tabbable@6.4.0: {}
+
+ tinyglobby@0.2.16:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ trim-lines@3.0.1: {}
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.1.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
+ vite@7.3.2:
+ dependencies:
+ esbuild: 0.27.7
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+ postcss: 8.5.10
+ rollup: 4.60.1
+ tinyglobby: 0.2.16
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ vitepress-plugin-comment-with-giscus@1.1.15(vue@3.5.32):
+ dependencies:
+ '@giscus/vue': 2.4.0(vue@3.5.32)
+ transitivePeerDependencies:
+ - vue
+
+ vitepress@2.0.0-alpha.17(postcss@8.5.10):
+ dependencies:
+ '@docsearch/css': 4.6.2
+ '@docsearch/js': 4.6.2
+ '@docsearch/sidepanel-js': 4.6.2
+ '@iconify-json/simple-icons': 1.2.78
+ '@shikijs/core': 3.23.0
+ '@shikijs/transformers': 3.23.0
+ '@shikijs/types': 3.23.0
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 6.0.6(vite@7.3.2)(vue@3.5.32)
+ '@vue/devtools-api': 8.1.1
+ '@vue/shared': 3.5.32
+ '@vueuse/core': 14.2.1(vue@3.5.32)
+ '@vueuse/integrations': 14.2.1(focus-trap@8.0.1)(vue@3.5.32)
+ focus-trap: 8.0.1
+ mark.js: 8.11.1
+ minisearch: 7.2.0
+ shiki: 3.23.0
+ vite: 7.3.2
+ vue: 3.5.32
+ optionalDependencies:
+ postcss: 8.5.10
+ transitivePeerDependencies:
+ - '@types/node'
+ - async-validator
+ - axios
+ - change-case
+ - drauu
+ - fuse.js
+ - idb-keyval
+ - jiti
+ - jwt-decode
+ - less
+ - lightningcss
+ - nprogress
+ - qrcode
+ - sass
+ - sass-embedded
+ - sortablejs
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - typescript
+ - universal-cookie
+ - yaml
+
+ vue@3.5.32:
+ dependencies:
+ '@vue/compiler-dom': 3.5.32
+ '@vue/compiler-sfc': 3.5.32
+ '@vue/runtime-dom': 3.5.32
+ '@vue/server-renderer': 3.5.32(vue@3.5.32)
+ '@vue/shared': 3.5.32
+
+ zwitch@2.0.4: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..4de91a3
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,2 @@
+packages:
+ - '.'
diff --git a/sw.js b/sw.js
deleted file mode 100644
index cf6295c..0000000
--- a/sw.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/* ===========================================================
- * docsify sw.js
- * ===========================================================
- * Copyright 2016 @huxpro
- * Licensed under Apache 2.0
- * Register service worker.
- * ========================================================== */
-
-const RUNTIME = 'docsify'
-const HOSTNAME_WHITELIST = [
- self.location.hostname,
- 'fonts.gstatic.com',
- 'fonts.googleapis.com',
- 'unpkg.com'
-]
-
-// The Util Function to hack URLs of intercepted requests
-const getFixedUrl = (req) => {
- var now = Date.now()
- var url = new URL(req.url)
-
- // 1. fixed http URL
- // Just keep syncing with location.protocol
- // fetch(httpURL) belongs to active mixed content.
- // And fetch(httpRequest) is not supported yet.
- url.protocol = self.location.protocol
-
- // 2. add query for caching-busting.
- // Github Pages served with Cache-Control: max-age=600
- // max-age on mutable content is error-prone, with SW life of bugs can even extend.
- // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
- // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
- if (url.hostname === self.location.hostname) {
- url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
- }
- return url.href
-}
-
-/**
- * @Lifecycle Activate
- * New one activated when old isnt being used.
- *
- * waitUntil(): activating ====> activated
- */
-self.addEventListener('activate', event => {
- event.waitUntil(self.clients.claim())
-})
-
-/**
- * @Functional Fetch
- * All network requests are being intercepted here.
- *
- * void respondWith(Promise r)
- */
-self.addEventListener('fetch', event => {
- // Skip some of cross-origin requests, like those for Google Analytics.
- if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
- // Stale-while-revalidate
- // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
- // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
- const cached = caches.match(event.request)
- const fixedUrl = getFixedUrl(event.request)
- const fetched = fetch(fixedUrl, { cache: 'no-store' })
- const fetchedCopy = fetched.then(resp => resp.clone())
-
- // Call respondWith() with whatever we get first.
- // If the fetch fails (e.g disconnected), wait for the cache.
- // If there’s nothing in cache, wait for the fetch.
- // If neither yields a response, return offline pages.
- event.respondWith(
- Promise.race([fetched.catch(_ => cached), cached])
- .then(resp => resp || fetched)
- .catch(_ => { /* eat any errors */ })
- )
-
- // Update the cache with the version we fetched (only for ok status)
- event.waitUntil(
- Promise.all([fetchedCopy, caches.open(RUNTIME)])
- .then(([response, cache]) => response.ok && cache.put(event.request, response))
- .catch(_ => { /* eat any errors */ })
- )
- }
-})
\ No newline at end of file