├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── code └── Algorithm │ ├── advanced │ └── .gitkeep │ ├── basic │ ├── 02-sort │ │ ├── bubblesort │ │ │ ├── BubbleSort.go │ │ │ └── BubbleSort_test.go │ │ ├── countingsort │ │ │ ├── CountingSort.go │ │ │ └── CountingSort_test.go │ │ ├── heapsort │ │ │ ├── HeapSort.go │ │ │ └── HeapSort_test.go │ │ ├── insertsort │ │ │ ├── InsertSort.go │ │ │ └── InsertSort_test.go │ │ ├── mergesort │ │ │ ├── MergeSort.go │ │ │ └── MergeSort_test.go │ │ ├── quicksort │ │ │ ├── QuickSort.go │ │ │ └── QuickSort_test.go │ │ └── selectsort │ │ │ ├── SelectSort.go │ │ │ └── SelectSort_test.go │ ├── 07-greedy │ │ ├── Greedy0-1.go │ │ └── Greedy0-1_test.go │ ├── 09-backtracking │ │ ├── 8queens │ │ │ ├── Queens8.go │ │ │ └── Queens8_test.go │ │ └── backtracking0-1 │ │ │ ├── Backtracking0-1.go │ │ │ └── Backtracking0-1_test.go │ └── 10-dynamic-programming │ │ ├── dynamic0-1.go │ │ └── dynamic0-1_test.go │ └── interview │ ├── ctci │ ├── 01-01-Is-Unique-LCCI │ │ ├── main.go │ │ └── main_test.go │ └── 01-02-Check-Permutation-LCCI │ │ ├── main.go │ │ └── main_test.go │ └── lcof │ ├── 24 │ ├── main.go │ └── main_test.go │ └── 06 │ ├── main.go │ └── main_test.go ├── docs ├── Algorithm │ ├── README.md │ └── basic │ │ ├── 01-complexion-analysis.md │ │ ├── 02-recursion.md │ │ ├── 03-sort.md │ │ ├── 04-hash.md │ │ ├── 07-greedy.md │ │ ├── 08-divide-conquer.md │ │ ├── 09-backtracking.md │ │ └── 10-dynamic-programming.md ├── Book-Summary │ └── README.md ├── Cloud-Native │ └── README.md ├── Container │ └── README.md ├── Data-Structure │ └── README.md ├── Databases │ └── README.md ├── Hot-Project │ └── README.md ├── Interview │ └── README.md ├── Language │ └── README.md ├── Middleware │ ├── README.md │ └── static │ │ └── kafka.png ├── Networking │ ├── Basic-Networking │ │ └── content.md │ └── README.md └── Operating-System │ ├── Direct-Execution │ └── Content.md │ ├── Process-and-process-Api │ ├── Content.md │ └── img │ │ ├── process_createtion_procedure.png │ │ ├── process_creation.png │ │ ├── process_status.png │ │ └── process_structure.png │ └── README.md ├── go.mod └── image ├── Algorithm ├── advanced │ └── .gitkeep └── basic │ ├── .gitkeep │ ├── 00-complexion-analysis │ └── 01-result-of-test.go.png │ ├── 01-recursion │ └── 01-fibonacci.jpg │ └── 09-backtracking │ └── 8queens.png ├── Networking └── Basic-Networking │ └── Protocol-Hierarchies.jpeg ├── douLinkedlist.png ├── logo.png ├── maxheap.png ├── minheap.png ├── queue.png ├── singLinkedList.png ├── singleRotaLeft1.png ├── singleRotaLeft2.png ├── singleRotaRight1.png ├── singleRotaRight2.png ├── stack.png └── tree.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report about existing problem 4 | title: "[Bug]: " 5 | labels: bug, documentation 6 | assignees: machitaoX, Rocksnake, zhyChesterCheung, sin-coder, Jacob953, LyliAgave, 7 | iFuon 8 | 9 | --- 10 | 11 | **Describe the bug** 12 | A clear and concise description of what the bug is. 13 | 14 | **Expected behavior** 15 | A clear and concise description of what you expected to happen. 16 | 17 | **Screenshots** 18 | If applicable, add screenshots to help explain your problem. 19 | 20 | **Location (please write the related information, e.g. Location):** 21 | - ./docs/Container/README.md [e.g. Location] 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an new feature for GoGetit 4 | title: "[Feat]: " 5 | labels: documentation, enhancement 6 | assignees: machitaoX, Rocksnake, zhyChesterCheung, sin-coder, Jacob953, LyliAgave, 7 | iFuon 8 | 9 | --- 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the feature is. Ex. I'm always frustrated when [...] 13 | 1. 14 | 2. 15 | 16 | **Describe the solution you'd like** 17 | A clear and concise description of what you want to happen. 18 | 19 | **Describe alternatives you've considered** 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | /.idea 4 | /.vscode 5 | 6 | .DS_Store 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Logo 4 | 5 |

6 | 7 | GoGetit 8 | 9 |

10 |

11 |
12 | 13 | 雄关漫道真如铁、而今迈步从头越 14 | 15 |

16 |

17 | 新手入门Go语言学习最佳指南与实践,涵盖Go基础语法、网络、操作系统、算法与数据结构、中间件、微服务、云原生、机器学习、大数据等领域全面指南与最佳实践 18 |

19 |
20 |
21 | 22 | issues 23 | 24 | 25 | fork 26 | 27 | 28 | star 29 | 30 | 31 | license 32 | 33 |
34 | 35 | # 1 项目背景 36 | 37 | ## 1.1 项目地址 38 | 39 | - [GoGetit](https://github.com/zhyChesterCheung/GoGetit) 40 | 41 | ## 1.2 项目介绍 42 | 43 | 目前,高校普遍使用Java或C++进行教学,且由于Java和C++生态发展较早,而Go起步较晚, 44 | 其生态并不非常完善而导致学习门槛较高;但Go语言由于其编写效率高、天然支持并发、工具链丰富、 45 | 支持跨平台编译等特性,非常适合分布式系统、内存数据库、云计算、中间件等领域的开发, 46 | 且已孵化出kubernetes、Docker、Prometheus等成熟项目。本项目意在帮助广大开发者朋友减少 47 | Go语言学习成本和信息搜索耗时,快速上手Go语言;介绍Go语言社区强大且日益完善的生态,以及 48 | 通过一系列代码帮助开发者们了解Go语言社区新特性与进展;同时通过Go实现分布式、操作系统、gRPC、 49 | Docker容器化、可观测性等最佳实践。 50 | 51 | # 2 项目目录 52 | 53 | 1. [Go基础](docs/Language/README.md) 54 | - 基础知识与概念 55 | - 基本语法 56 | - 基本数据类型 57 | - Go并发 58 | - Goruntine 59 | - Go新特性 60 | 2. [操作系统](docs/Operating-System/README.md) 61 | - OS基本概念 62 | - Go操作系统编程 63 | 3. [计算机网络](docs/Networking/README.md) 64 | - 网络基础知识 65 | - 网络分层 66 | - Go-HTTP 67 | - Go-RPC 68 | 4. [数据库](docs/Databases/README.md) 69 | - Go - 操作数据库 70 | - Go - 数据库驱动库 71 | - Go - 实现数据库 72 | - Go - 数据库辅助工具 73 | 5. [算法](docs/Algorithm/README.md) 74 | - Go - 基础篇 75 | - Go - 进阶篇 76 | - Go - 刷题指导 77 | - Go - 高频题总结 78 | 79 | 6. [数据结构](docs/Data-Structure/README.md) 80 | - 线性数据结构 81 | - 树-数据结构 82 | - 图-数据结构 83 | - 堆-数据结构 84 | - 其他-数据结构 85 | 7. [容器化](docs/Container/README.md) 86 | - 虚拟化基础知识 87 | - 容器化基础知识 88 | - Docker应用 89 | - Go-Docker开发 90 | 8. [中间件](docs/Middleware/README.md) 91 | - 消息队列 92 | - 缓存 93 | 9. [云计算&云原生](docs/Cloud-Native/README.md) 94 | - 云原生的前世今生 95 | - 微服务 96 | - 容器编排Kubernetes 97 | - 可观测性 98 | 10. [书籍汇总](docs/Book-Summary/README.md) 99 | 11. [推荐项目](docs/Hot-Project/README.md) 100 | 12. [高频面试题汇总](docs/Interview/README.md) 101 | 102 | ... 103 | 104 | # 3 项目成员 105 | 106 | GoGetit社区的发展离不开每个人的贡献,感谢你们! 107 | 108 | 109 | 110 | 111 | 112 | 113 | | 项目PMC成员 | GitHub ID | 114 | | ----------------------- | ---------------- | 115 | | csuzhang | [zhyChesterCheung](https://github.com/zhyChesterCheung) | 116 | | yanyuwei | [Rocksnake](https://github.com/Rocksnake) | 117 | | machitao | [machitaoX](https://github.com/machitaoX) | 118 | | sinyang | [sin-coder](https://github.com/sin-coder) | 119 | | yujunfeng | [Jacob953](https://github.com/Jacob953) | 120 | | fukangyue | [iFuon](https://github.com/iFuon) | 121 | | liruanjun | [LyliAgave](https://github.com/LyliAgave) | 122 | 123 | 124 | # 4 项目排期 125 | 126 | | 内容领域 | 认领排期 | 127 | | ------------- | -------- | 128 | | Go基础 | machitaoX | 129 | | 操作系统 | yanyuwei | 130 | | 计算机网络 | liruanjun | 131 | | 数据库 | sin-coder | 132 | | 算法 | jacob953 | 133 | | 数据结构 |fukangyue | 134 | | 容器化 | csuzhang | 135 | | 中间件 | machitaoX | 136 | | 云计算&云原生 | csuzhang | 137 | | 书籍汇总 | csuzhang | 138 | | 推荐项目 | csuzhang | 139 | | 高频面试题汇总 | | 140 | 141 | # 5 GitHub Label 142 | 143 | | 来自Github默认标签 | 描述 | 144 | | ------------------ | -------------------------------------- | 145 | | bug | 表示议题出现异常 | 146 | | documentation | 表示文档需要改进或补充 | 147 | | duplicate | 表示类似的议题或拉取请求 | 148 | | enhancement | 表示新功能申请 | 149 | | good first issue | 表示适用首次贡献者的议题 | 150 | | help wanted | 表示维护员需要议题或拉取请求方面的帮助 | 151 | | invalid | 表示议题或拉取请求不再相关 | 152 | | question | 表示议题或拉取请求需要更多信息 | 153 | | wontfix | 表示不会继续处理议题或拉取请求 | 154 | 155 | # 6 行为准则 156 | 157 | > 本项目遵循 [`Apache license`](https://github.com/zhyChesterCheung/GoGetit/blob/main/LICENSE) 158 | 159 | 作为这个项目的贡献者和维护者,为了建立一个开放和受欢迎的社区,我们保证尊重所有通过报告问题、发布功能请求、更新文档、提交拉取请求或补丁以及其他活动做出贡献的人员。 160 | 161 | 我们致力于让参与此项目的每个人都不受骚扰,无论其经验水平、性别、性别认同和表达、性取向、残疾、个人外貌、体型、人种、种族、年龄、宗教或国籍等。 162 | 163 | 不可接受的参与者行为包括: 164 | 165 | - 使用性语言或图像 166 | - 人身攻击 167 | - 挑衅、侮辱或贬低性评论 168 | - 公开或私下骚扰 169 | - 未经明确许可,发布他人的私人信息,比如地址或电子邮箱 170 | - 其他不道德或不专业的行为 171 | 172 | 项目维护者有权利和责任删除、编辑或拒绝评论、提交、代码、维基编辑、问题和其他不符合本行为准则的贡献。通过采用本行为准则,项目维护者承诺将这些原则公平且一致地应用到这个项目管理的各个方面。不遵守或不执行行为准则的项目维护者可能被永久地从项目团队中移除。 173 | 174 | # 7 联系我们 175 | 176 | Email: -------------------------------------------------------------------------------- /code/Algorithm/advanced/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/code/Algorithm/advanced/.gitkeep -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/bubblesort/BubbleSort.go: -------------------------------------------------------------------------------- 1 | package bubblesort 2 | 3 | func bubblesort(arr []int) []int { 4 | length := len(arr) 5 | if length <= 1 { 6 | return arr 7 | } 8 | 9 | for tail := length - 1; tail > 0; tail-- { 10 | var flag bool 11 | for i := 0; i < tail; i++ { 12 | if arr[i] > arr[i+1] { 13 | flag = true 14 | arr[i], arr[i+1] = arr[i+1], arr[i] 15 | } 16 | } 17 | if !flag { 18 | break 19 | } 20 | } 21 | return arr 22 | } 23 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/bubblesort/BubbleSort_test.go: -------------------------------------------------------------------------------- 1 | package bubblesort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_bubblesort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := bubblesort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("bubblesort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/countingsort/CountingSort.go: -------------------------------------------------------------------------------- 1 | package countingsort 2 | 3 | func CountingSort(arr []int) []int { 4 | arrLen := len(arr) 5 | if arrLen <= 1 { 6 | return arr 7 | } 8 | 9 | max := arr[0] 10 | for _, val := range arr { 11 | if val > max { 12 | max = val 13 | } 14 | } 15 | 16 | c := make([]int, max+1) 17 | 18 | for _, val := range arr { 19 | c[val]++ 20 | } 21 | 22 | for i := 1; i <= max; i++ { 23 | c[i] = c[i-1] + c[i] 24 | } 25 | 26 | r := make([]int, arrLen) 27 | for i := max - 1; i >= 0; i-- { 28 | index := c[arr[i]] - 1 29 | r[index] = arr[i] 30 | c[arr[i]]-- 31 | } 32 | 33 | copy(arr, r) 34 | 35 | return arr 36 | } 37 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/countingsort/CountingSort_test.go: -------------------------------------------------------------------------------- 1 | package countingsort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestCountingSort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := CountingSort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("CountingSort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/heapsort/HeapSort.go: -------------------------------------------------------------------------------- 1 | package heapsort 2 | 3 | func HeadSort(arr []int) []int { 4 | arrLen := len(arr) 5 | if arrLen <= 1 { 6 | return arr 7 | } 8 | 9 | buildheap(arr, arrLen-1) 10 | 11 | tail := arrLen - 1 12 | for tail > 0 { 13 | arr[0], arr[tail] = arr[tail], arr[0] 14 | tail-- 15 | heapify(arr, tail, 0) 16 | } 17 | 18 | return arr 19 | } 20 | 21 | func buildheap(arr []int, tail int) { 22 | for i := tail / 2; i >= 0; i-- { 23 | heapify(arr, tail, i) 24 | } 25 | } 26 | 27 | func heapify(arr []int, tail, i int) { 28 | maxPos := i 29 | for { 30 | if i*2 <= tail && arr[i] < arr[i*2] { 31 | maxPos = i * 2 32 | } 33 | if i*2+1 <= tail && arr[maxPos] < arr[i*2+1] { 34 | maxPos = i*2 + 1 35 | } 36 | if maxPos == i { 37 | return 38 | } 39 | arr[i], arr[maxPos] = arr[maxPos], arr[i] 40 | i = maxPos 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/heapsort/HeapSort_test.go: -------------------------------------------------------------------------------- 1 | package heapsort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestHeadSort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := HeadSort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("HeadSort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/insertsort/InsertSort.go: -------------------------------------------------------------------------------- 1 | package insertsort 2 | 3 | func insertsort(arr []int) []int { 4 | length := len(arr) 5 | if length <= 1 { 6 | return arr 7 | } 8 | for tail := 1; tail < length; tail++ { 9 | val := arr[tail] 10 | i := tail - 1 11 | for ; i >= 0; i-- { 12 | if arr[i] > val { 13 | arr[i+1] = arr[i] 14 | } else { 15 | break 16 | } 17 | } 18 | arr[i+1] = val 19 | } 20 | return arr 21 | } 22 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/insertsort/InsertSort_test.go: -------------------------------------------------------------------------------- 1 | package insertsort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_insertsort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := insertsort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("insertsort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/mergesort/MergeSort.go: -------------------------------------------------------------------------------- 1 | package mergesort 2 | 3 | func MergeSort(arr []int) []int { 4 | arrLen := len(arr) 5 | if arrLen <= 1 { 6 | return arr 7 | } 8 | 9 | mergesort(arr, 0, arrLen-1) 10 | return arr 11 | } 12 | 13 | func mergesort(arr []int, head, tail int) { 14 | if head >= tail { 15 | return 16 | } 17 | 18 | mid := (head + tail) / 2 19 | mergesort(arr, head, mid) 20 | mergesort(arr, mid+1, tail) 21 | merge(arr, head, mid, tail) 22 | } 23 | 24 | func merge(arr []int, head, mid, tail int) { 25 | tmpArr := make([]int, tail-head+1) 26 | 27 | i, j, k := head, mid+1, 0 28 | for ; i <= mid && j <= tail; k++ { 29 | if arr[i] <= arr[j] { 30 | tmpArr[k] = arr[i] 31 | i++ 32 | } else { 33 | tmpArr[k] = arr[j] 34 | j++ 35 | } 36 | } 37 | 38 | for ; i <= mid; i++ { 39 | tmpArr[k] = arr[i] 40 | k++ 41 | } 42 | for ; j <= tail; j++ { 43 | tmpArr[k] = arr[j] 44 | k++ 45 | } 46 | copy(arr[head:tail+1], tmpArr) 47 | } 48 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/mergesort/MergeSort_test.go: -------------------------------------------------------------------------------- 1 | package mergesort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestMergeSort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := MergeSort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("MergeSort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/quicksort/QuickSort.go: -------------------------------------------------------------------------------- 1 | package quicksort 2 | 3 | func QuickSort(arr []int) []int { 4 | arrLen := len(arr) 5 | if arrLen <= 1 { 6 | return arr 7 | } 8 | 9 | quicksort(arr, 0, arrLen-1) 10 | return arr 11 | } 12 | 13 | func quicksort(arr []int, head, tail int) { 14 | if head >= tail { 15 | return 16 | } 17 | 18 | mid := partition(arr, head, tail) 19 | quicksort(arr, head, mid) 20 | quicksort(arr, mid+1, tail) 21 | } 22 | 23 | func partition(arr []int, head, tail int) int { 24 | pivot := arr[tail] 25 | i := head 26 | for j := head; j < tail; j++ { 27 | if arr[j] < pivot { 28 | arr[i], arr[j] = arr[j], arr[i] 29 | i++ 30 | } 31 | } 32 | arr[i], arr[tail] = arr[tail], arr[i] 33 | return head 34 | } 35 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/quicksort/QuickSort_test.go: -------------------------------------------------------------------------------- 1 | package quicksort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestQuickSort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := QuickSort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("QuickSort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/selectsort/SelectSort.go: -------------------------------------------------------------------------------- 1 | package selectsort 2 | 3 | func selectsort(arr []int) []int { 4 | length := len(arr) 5 | if length <= 1 { 6 | return arr 7 | } 8 | for tail := 0; tail < length; tail++ { 9 | pos := tail 10 | for i := tail + 1; i < length; i++ { 11 | if arr[i] < arr[pos] { 12 | pos = i 13 | } 14 | } 15 | arr[tail], arr[pos] = arr[pos], arr[tail] 16 | } 17 | return arr 18 | } 19 | -------------------------------------------------------------------------------- /code/Algorithm/basic/02-sort/selectsort/SelectSort_test.go: -------------------------------------------------------------------------------- 1 | package selectsort 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_selectsort(t *testing.T) { 9 | type args struct { 10 | arr []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "[3, 1, 5, 4, 2]", 19 | args: args{ 20 | []int{3, 1, 5, 4, 2}, 21 | }, 22 | want: []int{1, 2, 3, 4, 5}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := selectsort(tt.args.arr); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("selectsort() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Algorithm/basic/07-greedy/Greedy0-1.go: -------------------------------------------------------------------------------- 1 | package greedy 2 | 3 | // 找到单价最大的下标 4 | func find_pos_max(prices []int) (pos_max int) { 5 | for i := 0; i < len(prices); i++ { 6 | if prices[pos_max] < prices[i] { 7 | pos_max = i 8 | } 9 | } 10 | return pos_max 11 | } 12 | 13 | func greedy(prices, stocks []int, bag int) (ans int) { 14 | pos_max, left := 0, bag 15 | 16 | find_pos_max(prices) 17 | 18 | for left != 0 { 19 | if stocks[pos_max] >= left { 20 | return ans + prices[pos_max]*left 21 | } 22 | ans += prices[pos_max] * stocks[pos_max] 23 | left -= stocks[pos_max] 24 | // 使用完存量后,只需要置零单价即可 25 | prices[pos_max] = 0 26 | pos_max = find_pos_max(prices) 27 | } 28 | 29 | return -1 30 | } 31 | -------------------------------------------------------------------------------- /code/Algorithm/basic/07-greedy/Greedy0-1_test.go: -------------------------------------------------------------------------------- 1 | package greedy 2 | 3 | import "testing" 4 | 5 | func Test_greedy(t *testing.T) { 6 | type args struct { 7 | s []int 8 | p []int 9 | bag int 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | wantAns int 15 | }{ 16 | { 17 | name: "0-1", 18 | args: args{ 19 | s: []int{10, 2, 20, 5}, 20 | p: []int{2, 5, 4, 8}, 21 | bag: 10, 22 | }, 23 | wantAns: 120, 24 | }, 25 | } 26 | for _, tt := range tests { 27 | t.Run(tt.name, func(t *testing.T) { 28 | if gotAns := greedy(tt.args.s, tt.args.p, tt.args.bag); gotAns != tt.wantAns { 29 | t.Errorf("greedy() = %v, want %v", gotAns, tt.wantAns) 30 | } 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Algorithm/basic/09-backtracking/8queens/Queens8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var result [8]int 6 | 7 | func cal8queens(row int) { 8 | if row == 8 { 9 | printQuees(result) 10 | return 11 | } 12 | 13 | for col := 0; col < 8; col++ { 14 | if isOK(row, col) { 15 | result[row] = col 16 | cal8queens(row + 1) 17 | } 18 | } 19 | } 20 | 21 | func isOK(row, col int) bool { 22 | leftup, rightup := col-1, col+1 23 | for i := row - 1; i >= 0; i-- { 24 | if result[i] == col { 25 | return false 26 | } 27 | if leftup >= 0 && result[i] == leftup { 28 | return false 29 | } 30 | if rightup < 8 && result[i] == rightup { 31 | return false 32 | } 33 | leftup-- 34 | rightup++ 35 | } 36 | return true 37 | } 38 | 39 | func printQuees(result [8]int) { 40 | for row := 0; row < 8; row++ { 41 | for col := 0; col < 8; col++ { 42 | if result[row] == col { 43 | fmt.Print("Q ") 44 | } else { 45 | fmt.Print("* ") 46 | } 47 | } 48 | fmt.Println() 49 | } 50 | fmt.Println() 51 | } 52 | -------------------------------------------------------------------------------- /code/Algorithm/basic/09-backtracking/8queens/Queens8_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func Test_cal8queens(t *testing.T) { 6 | type args struct { 7 | row int 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | }{ 13 | { 14 | name: "cal8queens(0)", 15 | args: args{ 16 | 0, 17 | }, 18 | }, 19 | } 20 | for _, tt := range tests { 21 | t.Run(tt.name, func(t *testing.T) { 22 | cal8queens(tt.args.row) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /code/Algorithm/basic/09-backtracking/backtracking0-1/Backtracking0-1.go: -------------------------------------------------------------------------------- 1 | package backtracking 2 | 3 | var max = 0 4 | 5 | func Backtracking(prices, weights []int, item, left, value int) int { 6 | // 背包剩余量为零或者已遍历所有物品 7 | if left == 0 || item == len(prices) { 8 | if max < value { 9 | max = value 10 | } 11 | return max 12 | } 13 | // 跳过当前物品 14 | max = Backtracking(prices, weights, item+1, left, value) 15 | // 在容量足够的情况下装下当前物品 16 | if left-weights[item] >= 0 { 17 | max = Backtracking(prices, weights, item+1, left-weights[item], value+prices[item]) 18 | } 19 | 20 | return max 21 | } 22 | -------------------------------------------------------------------------------- /code/Algorithm/basic/09-backtracking/backtracking0-1/Backtracking0-1_test.go: -------------------------------------------------------------------------------- 1 | package backtracking 2 | 3 | import "testing" 4 | 5 | func TestBacktracking(t *testing.T) { 6 | type args struct { 7 | prices []int 8 | weights []int 9 | item int 10 | left int 11 | value int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want int 17 | }{ 18 | { 19 | name: "0-1", 20 | args: args{ 21 | prices: []int{10, 8, 20, 5}, 22 | weights: []int{2, 5, 9, 3}, 23 | item: 0, 24 | left: 10, 25 | value: 0, 26 | }, 27 | want: 23, 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | if got := Backtracking(tt.args.prices, tt.args.weights, tt.args.item, tt.args.left, tt.args.value); got != tt.want { 33 | t.Errorf("Backtracking() = %v, want %v", got, tt.want) 34 | } 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /code/Algorithm/basic/10-dynamic-programming/dynamic0-1.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | func Dynamic(prices, weights []int, left int) (value int) { 4 | // lenSnack 表示决策阶段 5 | lenSnack := len(prices) 6 | // 初始化 states: 7 | // states[][] 表示总价值 8 | states := make([][]int, lenSnack) 9 | for i := 0; i < lenSnack; i++ { 10 | states[i] = make([]int, left+1) 11 | } 12 | if weights[0] <= left { 13 | states[0][weights[0]] = prices[0] 14 | } 15 | for i := 1; i < lenSnack; i++ { 16 | // 不选择第 i 个物品 17 | for j := 0; j <= left; j++ { 18 | if states[i-1][j] != 0 { 19 | states[i][j] = states[i-1][j] 20 | } 21 | } 22 | // 选择第 i 个物品 23 | for j := 0; j <= left-weights[i]; j++ { 24 | if states[i-1][j] >= 0 && states[i-1][j]+prices[i] > states[i][j+weights[i]] { 25 | states[i][j+weights[i]] = states[i-1][j] + prices[i] 26 | } 27 | } 28 | } 29 | // 找到最大的值 30 | for _, val := range states[lenSnack-1] { 31 | if val > value { 32 | value = val 33 | } 34 | } 35 | return value 36 | } 37 | -------------------------------------------------------------------------------- /code/Algorithm/basic/10-dynamic-programming/dynamic0-1_test.go: -------------------------------------------------------------------------------- 1 | package dynamic 2 | 3 | import "testing" 4 | 5 | func TestDynamic(t *testing.T) { 6 | type args struct { 7 | prices []int 8 | weights []int 9 | left int 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | wantValue int 15 | }{ 16 | { 17 | name: "0-1", 18 | args: args{ 19 | prices: []int{10, 8, 20, 5}, 20 | weights: []int{2, 5, 9, 3}, 21 | left: 10, 22 | }, 23 | wantValue: 23, 24 | }, 25 | } 26 | for _, tt := range tests { 27 | t.Run(tt.name, func(t *testing.T) { 28 | if gotValue := Dynamic(tt.args.prices, tt.args.weights, tt.args.left); gotValue != tt.wantValue { 29 | t.Errorf("Dynamic() = %v, want %v", gotValue, tt.wantValue) 30 | } 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Algorithm/interview/ctci/01-01-Is-Unique-LCCI/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func isUnique(astr string) bool { 4 | var result rune 5 | result = 0 6 | for _, ch := range astr { 7 | bit := ch - 97 8 | if result&(1<= 0; tail-- { 21 | res[tail] = tmp.Val 22 | tmp = tmp.Next 23 | } 24 | return res 25 | } 26 | -------------------------------------------------------------------------------- /code/Algorithm/interview/lcof/06/main_test.go: -------------------------------------------------------------------------------- 1 | // 执行用时:0 ms, 在所有 Go 提交中击败了100.00%的用户 2 | // 内存消耗:2.8 MB, 在所有 Go 提交中击败了96.36%的用户 3 | // 通过测试用例:24 / 24 4 | 5 | package main 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func Test_reversePrint(t *testing.T) { 13 | type args struct { 14 | head *ListNode 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want []int 20 | }{ 21 | // TODO: Add test cases. 22 | } 23 | for _, tt := range tests { 24 | t.Run(tt.name, func(t *testing.T) { 25 | if got := reversePrint(tt.args.head); !reflect.DeepEqual(got, tt.want) { 26 | t.Errorf("reversePrint() = %v, want %v", got, tt.want) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /code/Algorithm/interview/lcof/24/main.go: -------------------------------------------------------------------------------- 1 | // 执行用时:0 ms, 在所有 Go 提交中击败了100.00%的用户 2 | // 内存消耗:2.5 MB, 在所有 Go 提交中击败了100.00%的用户 3 | // 通过测试用例:27 / 27 4 | 5 | package main 6 | 7 | type ListNode struct { 8 | Val int 9 | Next *ListNode 10 | } 11 | 12 | func reverseList(head *ListNode) *ListNode { 13 | if head == nil { 14 | return head 15 | } 16 | tmp := head 17 | var pre *ListNode 18 | for next := head.Next; next != nil; next = next.Next { 19 | tmp.Next = pre 20 | pre = tmp 21 | tmp = next 22 | } 23 | tmp.Next = pre 24 | return tmp 25 | } 26 | -------------------------------------------------------------------------------- /code/Algorithm/interview/lcof/24/main_test.go: -------------------------------------------------------------------------------- 1 | // 执行用时:0 ms, 在所有 Go 提交中击败了100.00%的用户 2 | // 内存消耗:2.5 MB, 在所有 Go 提交中击败了100.00%的用户 3 | // 通过测试用例:27 / 27 4 | 5 | package main 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func Test_reverseList(t *testing.T) { 13 | type args struct { 14 | head *ListNode 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want *ListNode 20 | }{ 21 | // TODO: Add test cases. 22 | } 23 | for _, tt := range tests { 24 | t.Run(tt.name, func(t *testing.T) { 25 | if got := reverseList(tt.args.head); !reflect.DeepEqual(got, tt.want) { 26 | t.Errorf("reverseList() = %v, want %v", got, tt.want) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/Algorithm/README.md: -------------------------------------------------------------------------------- 1 | # 算法 - Algorithm implement by Go 2 | 3 | > **Agreement**:为了避免争议,专业名词会尽可能使用英文,翻译供辅助参考 4 | 5 | - [算法 - Algorithm implement by Go](#算法---algorithm-implement-by-go) 6 | - [Go - 前言](#go---前言) 7 | - [Go - 基础篇](#go---基础篇) 8 | - [Go - 进阶篇](#go---进阶篇) 9 | - [Go - 刷题指导](#go---刷题指导) 10 | - [Go - 高频题总结](#go---高频题总结) 11 | 12 | ## Go - 前言 13 | 14 | 在进行算法学习前,我想要先明确一下算法与数据结构之间的关系: 15 | 16 | - 数据结构是为算法服务的 17 | - 算法是需要建立在特定的数据结构之上的 18 | 19 | 所以,如果你还没有学过数据结构,我希望你能够先参考一下: 20 | [Go 数据结构](https://github.com/Superego-CodeEngineer/GoGetit/blob/main/docs/Data-Structure/README.md) 21 | 22 | 同时,本算法模块将全程使用 Go 语言实现,如果你对 Go 的掌握程度还不够自信,也可以参考一下: 23 | [Go 基础](https://github.com/Superego-CodeEngineer/GoGetit/blob/main/docs/Language/README.md) 24 | 25 | 接下来,我会假设你已经有了一定的数据结构基础和 Go 编程基础 26 | 27 | 不过,在详细介绍算法之前,我认为还有一样十分重要的内容需要明确: 28 | 29 | - 对于算法方面的研究,复杂的数学推理和证明是非常重要的,但是,本算法模块可能并不太适用于这类需求 30 | 31 | - 本算法模块不会过度关注数学性的推导,而是由浅入深,逐步分析不同算法的具体的环境,给予最直观的感受 32 | 33 | 因此,请放心,接下来的内容不需要过于强悍的数学基础 34 | 35 | ## Go - 基础篇 36 | 37 | > 文中涉及到的算法将会一一列举在[这里](../../code/Algorithm/basic/),你可以使用 go test 命令进行测试 38 | 39 | 1. [Analysis of Complexion - 复杂度分析](basic/01-complexion-analysis.md) 40 | - Go - 复杂度分析的意义 41 | - Go - 大 O 复杂度表示法 42 | - Go - 时间复杂度分析 43 | - Go - 空间复杂度分析 44 | - Go - 常见的复杂度量级 45 | 46 | 2. [Recursion - 递归](basic/02-recursion.md) 47 | - Go - 什么是递归? 48 | - Go - 递归的三个条件 49 | - Go - 递归的注意事项 50 | 51 | 3. [Sort - 排序](basic/03-sort.md) 52 | - Go - 排序算法的指标 53 | - Go - 排序算法的分类 54 | 55 | 4. [Hash - 哈希](basic/04-hash.md) 56 | - Go - 哈希算法的定义 57 | - Go - 哈希算法的特征 58 | - Go - 哈希算法的应用 59 | 60 | 5. [Search - 搜索](basic/05-search.md) 61 | 62 | 6. [String Matching - 字符串匹配](basic/06-string-matching.md) 63 | 64 | 7. [Greedy - 贪心](basic/07-greedy.md) 65 | - Go - 贪心算法的通常适用场景 66 | - Go - 贪心算法的核心思想 67 | - Go - 贪心算法与最优解的关系 68 | - Go - 贪心算法的应用 69 | 70 | 8. [Divide & Conquer - 分治](basic/08-divide-conquer.md) 71 | - Go - 分治算法的三个步骤 72 | - Go - 分治算法的条件 73 | - Go - 分治算法的应用 74 | 75 | 9. [Backtracking - 回溯](basic/09-backtracking.md) 76 | - Go - 经典的八皇后问题 77 | - Go - 回溯的核心思想 78 | - Go - 回溯算法的应用 79 | 80 | 10. [Dynamic Programming - 动态规划](basic/10-dynamic-programming.md) 81 | - Go - 动态规划的通用模型 82 | - Go - 动态规划的三个特征 83 | - Go - 动态规划的两种方法 84 | - Go - 动态规划的应用 85 | 86 | ## Go - 进阶篇 87 | 88 | ## Go - 刷题指导 89 | 90 | ## Go - 高频题总结 91 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/01-complexion-analysis.md: -------------------------------------------------------------------------------- 1 | # 1. Analysis of Complexion - 复杂度分析 2 | 3 | - [1. Analysis of Complexion - 复杂度分析](#1-analysis-of-complexion---复杂度分析) 4 | - [Go - 复杂度分析的意义](#go---复杂度分析的意义) 5 | - [Go - 大 O 复杂度表示法](#go---大-o-复杂度表示法) 6 | - [Go - 时空复杂度](#go---时空复杂度) 7 | - [Go - 时间复杂度](#go---时间复杂度) 8 | - [Go - 空间复杂度](#go---空间复杂度) 9 | - [Go - 常见的复杂度量级](#go---常见的复杂度量级) 10 | - [Go - 时间复杂度分析](#go---时间复杂度分析) 11 | 12 | 在学习完数据结构之后,相信你能感受到特定环境下,特定数据结构带来的高效性 13 | 14 | 算法和数据结构也是一样,它们本身就是为了解决代码的运行速度和存储空间消耗的问题 15 | 16 | 这个问题所分析的对象,就是我们常说的时空复杂度,即时间复杂度和空间复杂度 17 | 18 | ## Go - 复杂度分析的意义 19 | 20 | 通常情况下,想要知道一段程序的运行时间,在 Golang 中可以配合以下代码进行计算: 21 | 22 | ```go 23 | import "time" 24 | 25 | // get start 26 | start := time.Now() 27 | 28 | // 29 | 30 | // time cost from start 31 | cost := time.Since(start) 32 | ``` 33 | 34 | 通过类似的统计、监控,可以得到代码执行的时间和内存占用大小,但是,这种基于测算的事后统计法局限性极大,它非常依赖测试环境、数据规模等其他环境因素等影响 35 | 36 | 譬如,对同一段代码进行计算,可以得到如下结果: 37 | 38 | 01-result-of-test.go 39 | 40 | 为此,我们需要一种更加粗粒度的方法来对复杂度进行分析,这就是常被提到的大 O 复杂度表示法 41 | 42 | > 注:真实世界的复杂度远不止时空复杂度这么简单,但为了方便理解,本章以时间复杂度的分析为主 43 | 44 | ## Go - 大 O 复杂度表示法 45 | 46 | 因为只需要做粗略计算,我们假设: 47 | 48 | - 代码的执行时间用 T(n) 来表示 49 | - 代码的执行次数用 f(n) 来表示 50 | - 每行语句的执行效率是 unit-time,通常将其假设为单位 1 51 | 52 | 以如下代码段为例: 53 | 54 | ```go 55 | func cal(n int) { 56 | //Loop 1 57 | i := 1 58 | for ; i <= n; i++ { 59 | //Loop 2 60 | for j:= 1; j <= n ; j++{ 61 | fmt.Println("i: ", i,"j: ", j) 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | 在 Loop 2 中,每行语句需要执行 n 遍,即 f(n) = 2 _ n ,在 Loop 1 中,Loop 2 需要执行 n 遍,那么这段代码的执行时间 T(n) = (( 2 _ n2 ) + 1 ) \* 1( 前 1 是 i 初始化,后 1 是 unit-time )。 68 | 69 | 很容易看出,T(n) 与 f(n) 是成正比的,于是,可以总结:**T(n) = O(f(n))**。 70 | 71 | 因此,类似 T(n) = O(2 \* n2) 这样的表达式被称为大 O 时间复杂度表示法。 72 | 73 | ## Go - 时空复杂度 74 | 75 | ### Go - 时间复杂度 76 | 77 | 大 O 时间复杂度只能粗略地描述代码执行时间随数据规模增长的变化趋势,故也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。 78 | 79 | 想要精益求精,优化自己的代码,降低其时间复杂度,首先就要学会如何分析时间复杂度: 80 | 81 | 1. 基本法则:只关注 f(n) 最多的代码段。 82 | 83 | T(n) = O(f(1) + f(1000000) + f(n)) = O(f(n)) 84 | 85 | 1. 加法法则:O(n) 是量级最大的 f(n) 的代码段之和。 86 | 87 | T(n) = T(f(2 \* n)) + T(f(n2)) = O(n2) 88 | T(n) = T(g(2 \* n)) + T(f(n2)) = O(max(O(g(n)), O(f(n))) 89 | 90 | 1. 乘法法则:O(n) 是嵌套代码段的 f(n) 之积。 91 | 92 | T(n) = T(g(n)) _ T(f(2 _ n)) = O(2 \* n2) 93 | 94 | 1. 保留法则:保留不同的数据规模 95 | 96 | 加法法则失效:T(n) = T(f(n)) + T(f(m)) = O(m + n) 97 | 乘法法则有效:T(n) = T(f(n)) _ T(f(m)) = O(m _ n) 98 | 99 | ### Go - 空间复杂度 100 | 101 | 大 O 表示法同样也可以用来表示空间复杂度,当其粗略地描述代码的存储空间与数据规模之间的增长关系时,就被称为渐进空间复杂度(asymptotic space complexity),简称空间复杂度。 102 | 103 | 空间复杂度的分析通过类比时间复杂度的分析即可,只需要记住,现在的关注点在于算法的存储空间,即运行代码所需要使用的内存。 104 | 105 | 如以下片段: 106 | 107 | ```go 108 | func cal() { 109 | // ... 110 | // 111 | n := [5]int{1, 2, 3, 4, 5} 112 | // ... 113 | // 114 | } 115 | ``` 116 | 117 | 无论其他代码段如何,只要不涉及存储空间,那么 f(n) = 5。 118 | 119 | ## Go - 常见的复杂度量级 120 | 121 | 代码段的写法千变万化,但常见的复杂度量级并不多,通常可以分为非多项式量级和多项式量级。 122 | 123 | 非多项式量级:O(2n) 和 O(n!) 124 | 125 | 非多项式量级的算法问题通常被叫做 NP 问题,即 Non-Deterministic Polynomial,这类问题几乎只有做此类算法研究的同学才会涉及,因此,本章将重心放在多项式量级: 126 | 127 | - 常量级:O(1) 128 | 129 | 只要不含循环、递归语句:T(n) = T(2) + T(100000) = O(1) 130 | 131 | - 线性、对数级:O(n), O(logn), O(nlogn) 132 | 133 | 大 O 表示法只需要做粗略计算,故忽略线性级的系数,对数级的底数: 134 | 135 | - T(n) = T(2 _ n) + T(5 _ n) = O(n) 136 | - T(n) = T(log3n) = O(logn) 137 | 138 | 而 O(nlogn) 可以看作线性级和对数级的嵌套,即遵循了乘法法则。 139 | 140 | - 次方级:O(nk) 141 | 142 | 多个相同数据代码段的嵌套,如循环、递归。 143 | 144 | 对于空间复杂度而言,只需要掌握 O(1), O(n), O(n2) 即可。 145 | 146 | ## Go - 时间复杂度分析 147 | 148 | 同一段代码在不同情况下时间复杂度会出现量级差异,因此,为了更全面,更准确的描述代码的时间复杂度,在了解时间复杂度和常见复杂度量级的基础上,我们常常会讨论四个时间复杂度分析问题。 149 | 150 | 但实际上,大多数情况下是不需要区别分析它们的,最重要的是学习这种分析思维: 151 | 152 | 1. 最好情况时间复杂度(best case time complexity) 153 | 154 | 在最理想的情况下,执行这段代码的时间复杂度 155 | 156 | 2. 最坏情况时间复杂度(worst case time complexity) 157 | 158 | 在最糟糕的情况下,执行这段代码的时间复杂度 159 | 160 | 3. 平均情况时间复杂度(average case time complexity) 161 | 162 | 用代码在所有情况下执行的次数的加权平均值表示 163 | 164 | 4. 均摊时间复杂度(amortized time complexity) 165 | 166 | 这是一种特殊的平均时间复杂度。当绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上 167 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/02-recursion.md: -------------------------------------------------------------------------------- 1 | # 2. Recursion - 递归 2 | 3 | - [2. Recursion - 递归](#2-recursion---递归) 4 | - [Go - 什么是递归?](#go---什么是递归) 5 | - [Go - 递归的三个条件](#go---递归的三个条件) 6 | - [Go - 递归的注意事项](#go---递归的注意事项) 7 | 8 | 简单地介绍完了复杂度分析,接下来就要进入算法实战了。 9 | 10 | 递归,可以说是算法学习之路上最难理解的知识点之一,很多复杂的算法实现都需要运用递归的编程技巧。 11 | 12 | ## Go - 什么是递归? 13 | 14 | 即使是科班生也很难第一时间理解透递归的思路,但其实只要将其拆开来看,递归并不难。 15 | 16 | 递归实际上只涉及两个操作,“递”和“归”。从实际生活中去理解递归: 17 | 18 | 场景一:监考老师分发试卷,由于多印了很多试卷,可以靠手去掂量,对试卷粗略分组。 19 | 20 | 要求:将多余的试卷回收给教务处。 21 | 22 | 具体步骤: 23 | 24 | 1. 将试卷发给第一排的同学,让其只能向后传; 25 | 26 | 2. 每个人只能拿一张试卷,如果没到场,留在其桌上即可; 27 | 28 | 3. 直到最后一排的同学拿到试卷后,多余的试卷将由最后一排向第一排传; 29 | 30 | 4. 传到第一排,等待监考老师来收即可。 31 | 32 | > 这里为了帮助理解算法,我们没有让最后一排的同学直接叫老师,而是一排一排向前传 33 | 34 | 分析上述具体步骤,理解如下: 35 | 36 | 步骤 1 即递归算法的入口,试卷将一排一排的传递下去,这就是“递”; 37 | 38 | 步骤 2 即递归算法的多个子问题,下一排不管是否有人,都要留一张试卷在桌上; 39 | 40 | 步骤 3 即递归算法的终止条件,试卷传到最后一排后,则要被传回第一排,这就是“归”; 41 | 42 | 步骤 4 即递归算法的出口,在代码实现过程中,这里便回到了调用递归算法的主函数中。 43 | 44 | 以上思路的代码实现如下: 45 | 46 | ```go 47 | // LAST :最后一排 48 | // TOTAL :该组的分发试卷数 49 | func rest(pos int) int { 50 | if pos == LAST { 51 | return TOTAL - 1 52 | } 53 | return rest(pos+1) - 1 54 | } 55 | ``` 56 | 57 | ## Go - 递归的三个条件 58 | 59 | 1. 可分解成多个子问题 60 | 61 | 实现递归算法的第一件事就是将问题拆分为多个子问题,子问题即数据规模更小的问题,比如,将“试卷还剩多少”,分解为多个“下一排试卷还剩多少”的问题。 62 | 63 | 2. 子问题除数据规模不同,算法思路完全一样 64 | 65 | 场景一中,在传递给下一排时,可以使用同一种算法思路解决子问题,如“无论是否到场,留一张试卷在桌上”。 66 | 67 | 同样,我们可以根据不同场景更改算法思路,如场景二:不需要给没来的同学留试卷,直接传给再下一排。这样的话,算法也会跟着变化: 68 | 69 | ```go 70 | // LAST: 最后一排 71 | // TOTAL: 该组的分发试卷数 72 | // stu[]: 保存考生的到场状态,0 为未到场 73 | func rest(pos int) int { 74 | if pos == LAST { 75 | return TOTAL - 1 76 | } 77 | if stu[pos + 1] == 0 { 78 | if pos + 1 == LAST { 79 | return TOTAL - 1 80 | } 81 | return rest(pos+2) - 1 82 | } 83 | return rest(pos+1) - 1 84 | } 85 | ``` 86 | 87 | 3. 存在终止条件 88 | 89 | 推导出递归公式后,还需要找到递归的终止条件。递归之于循环一样,如果没有终止条件,算法将会无限递归下去,因此,在实现递归算法时,一定要找到合适的终止条件。 90 | 91 | ## Go - 递归的注意事项 92 | 93 | 1. 警惕内存溢出 94 | 95 | 如果你已经开始实现递归算法了,相信你肯定碰到过这样的问题 `fatal error: stack overflow` ,这便是因为一直进行函数调用,导致栈、堆内存溢出产生的错误。 96 | 97 | 最大允许的递归深度跟当前线程剩余的栈空间有关,不误进行事前计算,因此,这种错误很难控制和预防。 98 | 99 | 2. 警惕重复计算 100 | 101 | 先来看看每每提到递归,就必谈的斐波那契数的实现吧: 102 | 103 | > Fibonacci:由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出,如:1, 1, 2, 3, 5, 8, 13, 21, 34, ... 104 | 105 | ```go 106 | func fib(n int) int { 107 | if n == 1 { 108 | return 1 109 | } 110 | if n == 0 { 111 | return 0 112 | } 113 | return fib(n-1) + fib(n-2) 114 | } 115 | ``` 116 | 117 | 不难发现,fib(fib(n-1)-2) 与 fib(fib(n-2)-1) 所计算的值是相同的,以 fib(6) 为例,如 Figure 1. Fibonacci 所示,fib(2) 会被计算 5 次: 118 | 119 | Figure 1. fibonacci 120 | 121 | 如果求 fib(20),难以想象同样一个值会被重复计算多少次,因此,我们可以利用一些数据结构(如数组、散列表等)巧妙地避免这个问题: 122 | 123 | ```go 124 | // tmp[]: 存储中间值 125 | func fib(n int) (res int) { 126 | if n == 0 { 127 | return 0 128 | } 129 | if n == 1 { 130 | return 1 131 | } 132 | if tmp[n] != 0 { 133 | return tmp[n] 134 | } 135 | res = fib(n-1) + fib(n-2) 136 | tmp[n] = res 137 | return res 138 | } 139 | ``` 140 | 141 | 当然,为了避免重复计算,还有更加高级的方法,我提出以上方法,便是在这里抛砖引玉了。 142 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/03-sort.md: -------------------------------------------------------------------------------- 1 | # 3. Sort - 排序 2 | 3 | - [3. Sort - 排序](#3-sort---排序) 4 | - [Go - 排序算法的指标](#go---排序算法的指标) 5 | - [Go - 排序算法的执行效率](#go---排序算法的执行效率) 6 | - [Go - 排序算法的内存消耗](#go---排序算法的内存消耗) 7 | - [Go - 排序算法的稳定性](#go---排序算法的稳定性) 8 | - [Go - 排序算法的分类](#go---排序算法的分类) 9 | - [Go - 时间复杂度 O(n2) 级](#go---时间复杂度-onsup2sup-级) 10 | - [Go - 时间复杂度 O(nlogn) 级](#go---时间复杂度-onlogn-级) 11 | - [Go - 时间复杂度 O(n) 级](#go---时间复杂度-on-级) 12 | 13 | 排序是生活中最常见的算法,可能现在你还没有接触过排序算法,但当你深入学习时,你发现它有种莫名的熟悉感。 14 | 15 | 排序算法数不胜数,短短一节肯定无法面面俱到,本节只会对最经典的排序算法进行介绍。 16 | 17 | 在介绍排序算法之前,我还需要向你简单说明一下排序算法的衡量指标。 18 | 19 | ## Go - 排序算法的指标 20 | 21 | 通常情况下,我们需要对排序算法的执行效率、内存消耗和稳定性进行比较: 22 | 23 | ### Go - 排序算法的执行效率 24 | 25 | 1. 最好、最坏、平均情况时间复杂度 26 | 2. 时间复杂度的系数、常数、低阶 27 | 3. 比较次数和移动(交换)次数 28 | 29 | 现实中的排序算法应该充分考虑数据的特点,同样的算法,面对不同数据的表现往往是不同的。 30 | 31 | ### Go - 排序算法的内存消耗 32 | 33 | - 原地排序:空间复杂度为 O(1) 的排序算法,即不需要额外的存储空间 34 | 35 | 需要消耗额外内存的排序算法往往更加稳定、效果更优,但是内存并不是无限的,面对 TB 级的数据量,非原地排序算法就会显得格外奢侈。 36 | 37 | ### Go - 排序算法的稳定性 38 | 39 | - 前提条件:假设原序列中存在值相等的元素。 40 | - 判断依据:值相同的元素在排序算法结束后,原有的先后关系是否发生变化。 41 | 42 | 面对复杂的业务,当需要使用多种排序算法时,具有稳定性的算法着能继承前一次排序的结果。 43 | 44 | ## Go - 排序算法的分类 45 | 46 | 接下来,我会按照排序算法的执行效率来介绍: 47 | 48 | ### Go - 时间复杂度 O(n2) 级 49 | 50 | **冒泡排序** 51 | 52 | 实现步骤: 53 | 1. 检查原数组长度: 54 | - 大于 1 则需要排序,否则不需要排序,直接返回原函数 55 | 2. 遍历数组,对相邻的两个数据进行比较: 56 | - 每次只会对相邻的两个数据进行比较和交换 57 | - 经过一轮遍历,如果未交换过数据,说明此时数组已有序 58 | 59 | ```Golang 60 | func bubblesort(arr []int) []int { 61 | length := len(arr) 62 | if length <= 1 { 63 | return arr 64 | } 65 | 66 | for tail := length - 1; tail > 0; tail-- { 67 | var flag bool 68 | for i := 0; i < tail; i++ { 69 | if arr[i] > arr[i+1] { 70 | flag = true 71 | tmp := arr[i] 72 | arr[i] = arr[i+1] 73 | arr[i+1] = tmp 74 | } 75 | } 76 | if !flag { 77 | break 78 | } 79 | } 80 | return arr 81 | } 82 | ``` 83 | 84 | **插入排序** 85 | 86 | 实现步骤: 87 | 1. 检查原数组长度: 88 | - 大于 1 则需要排序,否则不需要排序,直接返回原函数 89 | 2. 将原数组视为两部分,已排序区间和未排序区间: 90 | - 在已排序区间中依次寻找插入数据的位置: 91 | - 大于插入数据则进行数据移动 92 | - 小于插入数据则跳出循环 93 | - 找到合适的位置插入数据 94 | 95 | ```Golang 96 | func insertsort(arr []int) []int { 97 | length := len(arr) 98 | if length <= 1 { 99 | return arr 100 | } 101 | for tail := 1; tail < length; tail++ { 102 | val := arr[tail] 103 | i := tail - 1 104 | for ; i >= 0; i-- { 105 | if arr[i] > val { 106 | arr[i+1] = arr[i] 107 | } else { 108 | break 109 | } 110 | } 111 | arr[i+1] = val 112 | } 113 | return arr 114 | } 115 | ``` 116 | 117 | **选择排序** 118 | 119 | 1. 检查原数组长度: 120 | - 大于 1 则需要排序,否则不需要排序,直接返回原函数 121 | 2. 将原数组视为两部分,已排序区间和未排序区间: 122 | - 在未排序区间中寻找值最小的数 123 | - 交换数据,使其添加到已排序区间的末尾 124 | 125 | ```Golang 126 | func selectsort(arr []int) []int { 127 | length := len(arr) 128 | if length <= 1 { 129 | return arr 130 | } 131 | for tail := 0; tail < length; tail++ { 132 | pos := tail 133 | for i := tail + 1; i < length; i++ { 134 | if arr[i] < arr[pos] { 135 | pos = i 136 | } 137 | } 138 | tmp := arr[tail] 139 | arr[tail] = arr[pos] 140 | arr[pos] = tmp 141 | } 142 | return arr 143 | } 144 | ``` 145 | 146 | 冒泡、插入、选择排序均是原地排序,但其中选择排序是不稳定的。 147 | 148 | 而将冒泡排序和插入排序进行比较,插入排序往往更受欢迎,虽然两者的元素移动次数都是原始数据的逆序度,但在进行数据交换的过程中,冒泡排序需要 3 个赋值操作,而插入排序仅需 1 个。 149 | 150 | 当然,插入排序还有优化空间,感兴趣的话,可以参考一下 [希尔排序](https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F) 151 | 152 | ### Go - 时间复杂度 O(nlogn) 级 153 | 154 | **归并排序** 155 | 156 | 实现步骤: 157 | 158 | 1. 把原数组从中间分成前后两部分 159 | 2. 对前后两部分分别排序 160 | 3. 将排好序的两部分合并在一起 161 | - 创建临时数组暂存已排序数据 162 | - 临时数组覆盖原数组 163 | 164 | ```Golang 165 | func MergeSort(arr []int) []int { 166 | arrLen := len(arr) 167 | if arrLen <= 1 { 168 | return arr 169 | } 170 | 171 | mergesort(arr, 0, arrLen-1) 172 | return arr 173 | } 174 | 175 | func mergesort(arr []int, head, tail int) { 176 | if head >= tail { 177 | return 178 | } 179 | 180 | mid := (head + tail) / 2 181 | mergesort(arr, head, mid) 182 | mergesort(arr, mid+1, tail) 183 | merge(arr, head, mid, tail) 184 | } 185 | 186 | func merge(arr []int, head, mid, tail int) { 187 | tmpArr := make([]int, tail-head+1) 188 | 189 | i, j, k := head, mid+1, 0 190 | for ; i <= mid && j <= tail; k++ { 191 | if arr[i] <= arr[j] { 192 | tmpArr[k] = arr[i] 193 | i++ 194 | } else { 195 | tmpArr[k] = arr[j] 196 | j++ 197 | } 198 | } 199 | 200 | for ; i <= mid; i++ { 201 | tmpArr[k] = arr[i] 202 | k++ 203 | } 204 | for ; j <= tail; j++ { 205 | tmpArr[k] = arr[j] 206 | k++ 207 | } 208 | copy(arr[head:tail+1], tmpArr) 209 | } 210 | ``` 211 | 212 | 我们可以很明显的发现,归并排序另外为临时数组开辟了新的空间,因此它并不是原地排序。 213 | 214 | 我们以归并排序为例,分析一下它的空间复杂度: 215 | 216 | 1. 通过递推公式求解,整个归并过程需要的复杂度是 O(nlogn) 217 | 2. 但实际上,在合并完成后,临时开辟的内存空间会被释放掉,一个函数执行时,只会使用一个内存空间 218 | 3. 因此,临时内存空间最大不会超过 n 个数据的大小,因此,空间复杂度应该为 O(n) 219 | 220 | **快速排序** 221 | 222 | 实现步骤: 223 | 224 | 1. 选取一个数作为 pivot 225 | 2. 将数组分为三部分: 226 | - 小于 pivot 的数放到左边 227 | - pivot 放在中间 228 | - 大于 pivot 的数放到右边 229 | 3. 分别对左右两个分区做相同的操作 230 | 231 | ```Golang 232 | package quicksort 233 | 234 | func QuickSort(arr []int) []int { 235 | arrLen := len(arr) 236 | if arrLen <= 1 { 237 | return arr 238 | } 239 | 240 | quicksort(arr, 0, arrLen-1) 241 | return arr 242 | } 243 | 244 | func quicksort(arr []int, head, tail int) { 245 | if head >= tail { 246 | return 247 | } 248 | 249 | mid := partition(arr, head, tail) 250 | quicksort(arr, head, mid) 251 | quicksort(arr, mid+1, tail) 252 | } 253 | 254 | func partition(arr []int, head, tail int) int { 255 | pivot := arr[tail] 256 | i := head 257 | for j := head; j < tail; j++ { 258 | if arr[j] < pivot { 259 | arr[i], arr[j] = arr[j], arr[i] 260 | i++ 261 | } 262 | } 263 | arr[i], arr[tail] = arr[tail], arr[i] 264 | return head 265 | } 266 | ``` 267 | 268 | 尽管归并和快排都采用了分治的思想,但它们的处理过程是截然相反的: 269 | 270 | - 归并自下而上,先排序,再合并 271 | - 快排自上而下,先分区,再排序 272 | 273 | 快排是一种不稳定的排序,但它巧妙的设计,使其成为原地排序,相比归并排序而言,较高的空间复杂度成为了它致命的缺陷,这也是快排更受欢迎的原因 274 | 275 | 当然,快排并不是完美的,在分区极度不均匀的情况下,快排的时间复杂度将会退化到 O(n2) 276 | 277 | **堆排序** 278 | 279 | 实现步骤: 280 | 281 | 1. 建堆 282 | - 自上而下进行 **堆化** 283 | 2. 排序 284 | - 从后向前排 285 | 286 | ```Golang 287 | func HeadSort(arr []int) []int { 288 | arrLen := len(arr) 289 | if arrLen <= 1 { 290 | return arr 291 | } 292 | 293 | buildheap(arr, arrLen-1) 294 | 295 | tail := arrLen - 1 296 | for tail > 0 { 297 | arr[0], arr[tail] = arr[tail], arr[0] 298 | tail-- 299 | heapify(arr, tail, 0) 300 | } 301 | 302 | return arr 303 | } 304 | 305 | func buildheap(arr []int, tail int) { 306 | for i := tail / 2; i >= 0; i-- { 307 | heapify(arr, tail, i) 308 | } 309 | } 310 | 311 | func heapify(arr []int, tail, i int) { 312 | maxPos := i 313 | for { 314 | if i*2 <= tail && arr[i] < arr[i*2] { 315 | maxPos = i * 2 316 | } 317 | if i*2+1 <= tail && arr[maxPos] < arr[i*2+1] { 318 | maxPos = i*2 + 1 319 | } 320 | if maxPos == i { 321 | return 322 | } 323 | arr[i], arr[maxPos] = arr[maxPos], arr[i] 324 | i = maxPos 325 | } 326 | } 327 | ``` 328 | 329 | 同样是原地排序,但相比快排,堆排序的时间复杂度非常稳定 O(nlogn) 330 | 331 | 但堆排序的性能往往没有快排优秀: 332 | 333 | 1. 堆排序的数据访问方式对 CPU 缓存很不友好,它不如快排那般是局部顺序访问 334 | 2. 堆排序的数据交换次数往往高于快排,并且建堆的过程很容易降低其有序度 335 | 336 | 337 | ### Go - 时间复杂度 O(n) 级 338 | 339 | 线性排序的复杂度似乎是不可思议的,最主要的原因在于同之前介绍的排序相比,它是非基于比较的排序算法 340 | 341 | **桶排序** 342 | 343 | > 桶排序和计数排序十分相似,为了方便测试,我将以计数排序为例展示代码 344 | 345 | 核心思想: 346 | 347 | 1. 将数据分到 m 个有序的桶里 348 | 2. 对每个桶的数据单独进行排序 349 | 3. 排序结束后依次取出 350 | 351 | 352 | **计数排序** 353 | 354 | > 这是桶排序的一种特殊情况 355 | 356 | ```Golang 357 | func CountingSort(arr []int) []int { 358 | arrLen := len(arr) 359 | if arrLen <= 1 { 360 | return arr 361 | } 362 | 363 | max := arr[0] 364 | for _, val := range arr { 365 | if val > max { 366 | max = val 367 | } 368 | } 369 | 370 | c := make([]int, max+1) 371 | 372 | for _, val := range arr { 373 | c[val]++ 374 | } 375 | 376 | for i := 1; i <= max; i++ { 377 | c[i] = c[i-1] + c[i] 378 | } 379 | 380 | r := make([]int, arrLen) 381 | for i := max - 1; i >= 0; i-- { 382 | index := c[arr[i]] - 1 383 | r[index] = arr[i] 384 | c[arr[i]]-- 385 | } 386 | 387 | return r 388 | } 389 | ``` 390 | 391 | **基数排序** 392 | 393 | 核心思想: 394 | 395 | 1. 将原数据补齐到同一长度 396 | 2. 分割成位进行稳定排序 397 | 398 | 基数排序对数据的要求很特别: 399 | 400 | 1. 位之间存在递进关系 401 | 2. 每一位的数据范围要能满足线性排序的要求 -------------------------------------------------------------------------------- /docs/Algorithm/basic/04-hash.md: -------------------------------------------------------------------------------- 1 | # 4. Hash - 哈希 2 | 3 | - [4. Hash - 哈希](#4-hash---哈希) 4 | - [Go - 哈希算法的定义](#go---哈希算法的定义) 5 | - [Go - 哈希算法的特征](#go---哈希算法的特征) 6 | - [Go - 哈希算法的应用](#go---哈希算法的应用) 7 | 8 | 看到 Hash 是否会感到疑惑,在数据结构的章节中我们也见过这个单词,不过在数据结构中,我们往往称之为散列表 9 | 10 | 哈希算法在生活中的应用很广,但在实际中基本有一套公认的标准,因此,本章仅对 Hash 做理论上的解释 11 | 12 | ## Go - 哈希算法的定义 13 | 14 | 哈希算法的定义非常简单,用一句话即可概括:将任意长度的二进制值串映射为固定长度的二进制值串 15 | 16 | ## Go - 哈希算法的特征 17 | 18 | 想要设计一个优秀的哈希算法,首先,需要满足一下特征: 19 | 1. 单项推导 20 | 2. 数据敏感 21 | 3. 冲突概率低 22 | 4. 高效执行 23 | 24 | ## Go - 哈希算法的应用 25 | 26 | 1. 安全加密 27 | 2. 哈希函数 28 | 3. 负载均衡 29 | 4. 分布式存储 30 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/07-greedy.md: -------------------------------------------------------------------------------- 1 | # 7. Greedy - 贪心 2 | 3 | > 掌握基础的数据结构和算法后,我还为你准备四种更加基本的、常用的算法:分治算法、回溯算法、贪心算法和动态规划 4 | > 5 | > 与其说是算法,不如说它们是算法思想,因为它们不是具体的算法,而是常作为我们设计具体算法的指导 6 | 7 | - [7. Greedy - 贪心](#7-greedy---贪心) 8 | - [Go - 贪心算法的通常适用场景](#go---贪心算法的通常适用场景) 9 | - [Go - 贪心算法的核心思想](#go---贪心算法的核心思想) 10 | - [Go - 贪心算法与最优解的关系](#go---贪心算法与最优解的关系) 11 | - [Go - 贪心算法的应用](#go---贪心算法的应用) 12 | 13 | 贪心算法可以说是生活中最常见的一种思想 14 | 15 | ## Go - 贪心算法的通常适用场景 16 | 17 | 贪心算法的适用场景往往具有两个特征: 18 | 19 | 1. 同时包含限制值和期望值 20 | 2. 要求在满足限制值的情况下,期望值最大 21 | 22 | ## Go - 贪心算法的核心思想 23 | 24 | 顾名思义,该算法的核心在于如何去贪,实际上很简单: 25 | 26 | - 每一步都保证期望值最大 27 | 28 | 但实际上,保证每一步期望值最大,并不能代表结果的期望值最大。 29 | 因此,贪心算法真正困难的地方在于如何平衡与最优解的关系 30 | 31 | ## Go - 贪心算法与最优解的关系 32 | 33 | 想要从理论上严格证明贪心算法的正确性是十分复杂的, 34 | 但从绝大部分实践的情况来看,贪心算法的正确性又是显而易见 35 | 36 | 假设我们有若干 10,9,1 面值的纸币,请问最少需要多少张纸币可以凑齐 36 元钱? 37 | 38 | - 从贪心的角度:3 张 10 元和 6 张 1 元的纸币是最优解 39 | - 但如果你对数字稍微敏感一点:4 张 9 元的纸币即可 40 | 41 | 因此,贪心算法并不能总是给出最优解 42 | 43 | ## Go - 贪心算法的应用 44 | 45 | 从 0-1 背包问题中理解贪心算法: 46 | 47 | 场景一:物品是可切割(或分块)的 48 | 49 | 背包能装 10kg 物品,从商店中选购如下几种散装的糖果: 50 | 1. s1: 10 RMB/kg 存量 2kg 51 | 2. s2: 2 RMB/kg 存量 5kg 52 | 3. s3: 20 RMB/kg 存量 4kg 53 | 4. s4: 5 RMB/kg 存量 8kg 54 | 55 | 如何购置才能让背包所装糖果的总值最高? 56 | 57 | ```Golang 58 | // 找到单价最大的下标 59 | func find_pos_max(prices []int) (pos_max int) { 60 | for i := 0; i < len(prices); i++ { 61 | if prices[pos_max] < prices[i] { 62 | pos_max = i 63 | } 64 | } 65 | return pos_max 66 | } 67 | 68 | func greedy(prices, stocks []int, bag int) (ans int) { 69 | pos_max, left := 0, bag 70 | 71 | find_pos_max(prices) 72 | 73 | for left != 0 { 74 | if stocks[pos_max] >= left { 75 | return ans + prices[pos_max]*left 76 | } 77 | ans += prices[pos_max] * stocks[pos_max] 78 | left -= stocks[pos_max] 79 | // 使用完存量后,只需要置零单价即可 80 | prices[pos_max] = 0 81 | pos_max = find_pos_max(prices) 82 | } 83 | 84 | return -1 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/08-divide-conquer.md: -------------------------------------------------------------------------------- 1 | # 8. Analysis of Complexion - 分治 2 | 3 | - [8. Analysis of Complexion - 分治](#8-analysis-of-complexion---分治) 4 | - [Go - 分治算法的三个步骤](#go---分治算法的三个步骤) 5 | - [Go - 分治算法的条件](#go---分治算法的条件) 6 | - [Go - 分治算法的应用](#go---分治算法的应用) 7 | 8 | 早在递归一节,我们便见识过了分治算法,这里需要再区别一下两者间的关系: 9 | 10 | - 分治算法是处理问题的思想 11 | - 递归是一种编程技巧 12 | 13 | ## Go - 分治算法的三个步骤 14 | 15 | 1. 分解:将原问题分解为一系列子问题 16 | 2. 解决:递归地求解各个子问题,并且当子问题足够小时,能直接求解 17 | 3. 合并:将子问题的结果合并成原问题 18 | 19 | ## Go - 分治算法的条件 20 | 21 | 1. 原问题与分解产生的子问题具有相同的模式 22 | 2. 子问题可以独立求解,且子问题之间没有相关性 23 | 3. 具有分解终止条件 24 | 4. 可以将子问题合并成原问题 25 | 5. 合并操作的复杂度不能太高 26 | 27 | ## Go - 分治算法的应用 28 | 29 | 从归并排序中理解分治算法:[归并排序](03-sort.md#go---时间复杂度-onlogn-级) 30 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/09-backtracking.md: -------------------------------------------------------------------------------- 1 | # 9. Backtracking - 回溯 2 | 3 | - [9. Backtracking - 回溯](#9-backtracking---回溯) 4 | - [Go - 经典的八皇后问题](#go---经典的八皇后问题) 5 | - [Go - 回溯的核心思想](#go---回溯的核心思想) 6 | - [Go - 回溯算法的应用](#go---回溯算法的应用) 7 | 8 | 谈到回溯算法,就一定要讨论最著名的八皇后问题,我会在文首给出答案,希望你能在理解回溯算法后回到这里看懂它: 9 | 10 | ## Go - 经典的八皇后问题 11 | 12 | 八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后? 13 | 14 | 为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上 15 | 16 | ![Figure.1 8queens](/image/Algorithm/basic/09-backtracking/8queens.png) 17 | 18 | ```Golang 19 | var result [8]int 20 | 21 | func cal8queens(row int) { 22 | if row == 8 { 23 | printQuees(result) 24 | return 25 | } 26 | 27 | for col := 0; col < 8; col++ { 28 | if isOK(row, col) { 29 | result[row] = col 30 | cal8queens(row + 1) 31 | } 32 | } 33 | } 34 | 35 | func isOK(row, col int) bool { 36 | leftup, rightup := col-1, col+1 37 | for i := row - 1; i >= 0; i-- { 38 | if result[i] == col { 39 | return false 40 | } 41 | if leftup >= 0 && result[i] == leftup { 42 | return false 43 | } 44 | if rightup < 8 && result[i] == rightup { 45 | return false 46 | } 47 | leftup-- 48 | rightup++ 49 | } 50 | return true 51 | } 52 | ``` 53 | 54 | ## Go - 回溯的核心思想 55 | 56 | 回溯就像时间宝石,能够支撑你: 57 | 58 | - 枚举所有的可能满足预期的选择 59 | - 在碰到不符合预期的解时倒退至上一个选择 60 | - 直到选择符合预期的解 61 | 62 | ## Go - 回溯算法的应用 63 | 64 | 我们已在 [Go - 贪心算法的应用](07-greedy.md#go---贪心算法的应用) 探讨过 0-1 背包问题,现在,再次从该问题中理解回溯算法: 65 | 66 | 场景二:物品是不可切割(或分块)的 67 | 68 | 背包能装 10kg 物品,从商店中选购如下几种零食: 69 | 1. s1: 10 RMB, 2kg 70 | 2. s2: 8 RMB, 5kg 71 | 3. s3: 20 RMB, 9kg 72 | 4. s4: 5 RMB, 3kg 73 | 74 | 如何购置才能让背包所装糖果的总值最高? 75 | 76 | ```Golang 77 | var max = 0 78 | 79 | func Backtracking(prices, weights []int, item, left, value int) int { 80 | // 背包剩余量为零或者已遍历所有物品 81 | if left == 0 || item == len(prices) { 82 | if max < value { 83 | max = value 84 | } 85 | return max 86 | } 87 | // 跳过当前物品 88 | max = Backtracking(prices, weights, item+1, left, value) 89 | // 在容量足够的情况下装下当前物品 90 | if left-weights[item] >= 0 { 91 | max = Backtracking(prices, weights, item+1, left-weights[item], value+prices[item]) 92 | } 93 | 94 | return max 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /docs/Algorithm/basic/10-dynamic-programming.md: -------------------------------------------------------------------------------- 1 | # 10. Dynamic Programming - 动态规划 2 | 3 | - [10. Dynamic Programming - 动态规划](#10-dynamic-programming---动态规划) 4 | - [Go - 动态规划的通用模型](#go---动态规划的通用模型) 5 | - [Go - 动态规划的三个特征](#go---动态规划的三个特征) 6 | - [Go - 动态规划的两种方法](#go---动态规划的两种方法) 7 | - [Go - 动态规划的应用](#go---动态规划的应用) 8 | 9 | 动态规划一般用来解决最优问题 10 | 11 | ## Go - 动态规划的通用模型 12 | 13 | 解决问题的过程中需要经历多个决策阶段,且每个决策阶段都对应着一组状态 14 | 15 | ## Go - 动态规划的三个特征 16 | 17 | 1. 最优子结构 18 | 2. 无后效性 19 | 3. 重复子问题 20 | 21 | ## Go - 动态规划的两种方法 22 | 23 | 1. 状态转移表法 24 | 2. 状态转移方程法 25 | 26 | ## Go - 动态规划的应用 27 | 28 | 认真思考过 [Go - 回溯算法的应用](09-backtracking.md#go---回溯算法的应用) 后,相信你会发现有很多子问题是重复的。 29 | 现在,我们来看看动态规划是如何优化 0-1 问题的: 30 | 31 | ```Golang 32 | 33 | func Dynamic(prices, weights []int, left int) (value int) { 34 | // lenSnack 表示决策阶段 35 | lenSnack := len(prices) 36 | // 初始化 states: 37 | // states[][] 表示总价值 38 | states := make([][]int, lenSnack) 39 | for i := 0; i < lenSnack; i++ { 40 | states[i] = make([]int, left+1) 41 | } 42 | if weights[0] <= left { 43 | states[0][weights[0]] = prices[0] 44 | } 45 | for i := 1; i < lenSnack; i++ { 46 | // 不选择第 i 个物品 47 | for j := 0; j <= left; j++ { 48 | if states[i-1][j] != 0 { 49 | states[i][j] = states[i-1][j] 50 | } 51 | } 52 | // 选择第 i 个物品 53 | for j := 0; j <= left-weights[i]; j++ { 54 | if states[i-1][j] >= 0 && states[i-1][j]+prices[i] > states[i][j+weights[i]] { 55 | states[i][j+weights[i]] = states[i-1][j] + prices[i] 56 | } 57 | } 58 | } 59 | // 找到最大的值 60 | for _, val := range states[lenSnack-1] { 61 | if val > value { 62 | value = val 63 | } 64 | } 65 | return value 66 | } 67 | ``` 68 | 69 | 当然,回溯也可以使用在 [递归](02-recursion.md) 的优化方法——备忘表,以达到同样的目的 70 | -------------------------------------------------------------------------------- /docs/Book-Summary/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Book-Summary/README.md -------------------------------------------------------------------------------- /docs/Cloud-Native/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Cloud-Native/README.md -------------------------------------------------------------------------------- /docs/Container/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Container/README.md -------------------------------------------------------------------------------- /docs/Data-Structure/README.md: -------------------------------------------------------------------------------- 1 | # Data structure implement by GO 2 | 3 | ## 一、线性数据结构 4 | #### 1、数组 5 | #### 2、链表 6 | - 单链表 7 | 8 | ![singLinkedList](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/singLinkedList.png) 9 | - 双链表 10 | ![doub](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/douLinkedlist.png) 11 | #### 3、栈 12 | ![stack](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/stack.png) 13 | - 栈的应用: 14 | #### 4、队列 15 | - 单队列 16 | ![queue](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/queue.png) 17 | - 循环队列 18 | - 队列的应用: 19 | ## 二、树 20 | ![tree](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/tree.png) 21 | #### 1、树的基本概念 22 | 树的结点(node):包含一个数据元素及若干指向子树的分支。 23 | 24 | 根节点(root node):树的起始结点。 25 | 26 | 子结点(child node):结点的子树的根称为该结点的孩子。 27 | 28 | 子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙。 29 | 30 | 叶子结点:也叫终端结点,是度为 0 的结点。 31 | 32 | 树的深度:树中最大的结点层 33 | #### 2、树的应用 34 | #### 3、树的遍历 35 | 前序遍历 36 | 首先访问根,再先序遍历左(右)子树,最后先序遍历右(左)子树。 37 | 38 | 中序遍历 39 | 首先中序遍历左(右)子树,再访问根,最后中序遍历右(左)子树。 40 | 41 | 后序遍历 42 | 首先后序遍历左(右)子树,再后序遍历右(左)子树,最后访问根。 43 | 44 | #### 4、二叉树的基本概念 45 | - 查找 46 | - 删除:1. 2. 47 | - 添加 48 | #### 4. 平衡二叉树的基本概念(AVL) 49 | - 增删查改的维护 50 | - single/double rotation 51 | ## 三、堆 52 | #### 1、由优先队列引出堆的基本概念 53 | #### 2、最大堆 54 | ![maxheap](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/maxheap.png) 55 | #### 3、最小堆 56 | ![minheap](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/minheap.png) 57 | #### 3、堆排序 58 | ## 四、图 59 | #### 1、图的一些语术 60 | #### 2、图的储存方式 61 | #### 3、图的搜索 62 | - DFS 63 | - BFS 64 | ## 五、其他 65 | 66 | -------------------------------------------------------------------------------- /docs/Databases/README.md: -------------------------------------------------------------------------------- 1 | # Go Fuck DataBase 2 | 3 | ## 一、Go - DataBase Usage 4 | 5 | ### 1、关系型数据库(类MySQL) 6 | 7 | ### 2、KV数据库(类Redis) 8 | 9 | ### 3、文档数据库(MongoDB) 10 | 11 | ### 4、列存储数据库(Hbase) 12 | 13 | ### 5、图数据库(Neo4j) 14 | 15 | ## 二、Go - DataBase Driver 16 | 17 | ## 三、Go - DataBase Implement 18 | 19 | ### 1、关系型数据库 20 | 21 | ### 2、时序数据库 22 | 23 | ### 3、KV数据库 24 | 25 | ## 四、Go - DataBase Helper 26 | 27 | -------------------------------------------------------------------------------- /docs/Hot-Project/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Hot-Project/README.md -------------------------------------------------------------------------------- /docs/Interview/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Interview/README.md -------------------------------------------------------------------------------- /docs/Language/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Language/README.md -------------------------------------------------------------------------------- /docs/Middleware/README.md: -------------------------------------------------------------------------------- 1 | # 消息队列 2 | ### Kafka 3 | 1. 核心参数 4 | 2. 持久化 5 | 3. 常见面试题: 6 | - **如何保证消息不重复消费** 7 | - 造成消息重复消费的原因 8 | - 根本原因:在消息处理后未及时提交 offset 9 | - 由于网络延迟、机器高 cpu 占用等原因导致 kafka 认为此消费者已经处于假死状态,进行了 rebalance 10 | - 解决方案 11 | 1. 关闭消息的自动提交,改为手动提交,在拉取到消息之后、处理消息之前提交offset,但是这样就造成消息丢失的可能性,一般基建比较好的公司在大数据部门的支持下,在凌晨(服务低潮期) 12 | 通过定时任务的方式进行补足 13 | 2. 最有效:数据处理的时候做觅等,比如redis的set、数据库的主键均具有天然的觅等性 14 | - **如何保证消息不丢失** 15 | - 关闭自动提交,在数据处理完之后手动提交 offset 16 | - **如何保证消息顺序消费** 17 | 1. kafka 的分区设置为,这种方式强烈不建议使用,仅仅是八股文的解决方案。我们在选取 kafka 这种 mq 的时候大部分原因由于kafka的超高吞吐。这么高的吞吐就是因为分区的思想,现在分区变成 18 | 一个,与 kafka 的设计背离。 19 | 2. 在业务当中很难有全局有序性,更多的是局部有序性,比如一个订单的从点击、加购物车、下单、付款,我只要保证我的这四个步骤有序即可。所以真正的场景 20 | 是将这四步有序,我们不妨将这四步的有序操作放在 kafka 上游,合成一条日志消息发到 kafka ,这样就保证了严格有序,也没有牺牲任何吞吐 21 | 3. 加入我就是碰上全局有序的场景,这样的话放到下游去做,在日志消息中有顺序维度比如时间,让下游在自己的存储中使用时排序 22 | - **kafka如何能保证高吞吐** 23 | 1. 分区 24 | 2. pull方式拉取数据 25 | - **如何预估分区数量和大小** 26 | - //todo 27 | > 消息队列面试的时候,建议要结合自己的业务场景来说明,不能光背八股,还要尽量考虑周全,尤其是边界情况 28 | ### RabbitMQ 29 | ### RocketMQ 30 | ### 综合对比 31 | |特性|Kafka|RabbitMQ|RocketMQ| 32 | |---|-----|---------|---------| 33 | |qps|百万|万|万| 34 | |事务支持|支持|支持|支持| 35 | |社区活跃|活跃|活跃|一般| 36 | ||||| 37 | ||||| 38 | ||||| 39 | # 缓存 40 | ### 分布式缓存:Redis、Memcached 和 Zookeeper 41 | 1. Redis 42 | - **常见的五种数据结构及底层实现** 43 | - **bitmap的使用及优势场景** 44 | - **redis的"快"的原因** 45 | - **redis的持久化文件rbd和aof的原理及使用** 46 | - **redis三种集群模式** 47 | - **redis同步机制** 48 | - **redis流量和存储倾斜** 49 | - **布隆过滤器** 50 | - **redLock的优劣** 51 | 2. Memcached 52 | 3. Zookeeper 53 | ### 本地缓存:go-cache -------------------------------------------------------------------------------- /docs/Middleware/static/kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Middleware/static/kafka.png -------------------------------------------------------------------------------- /docs/Networking/Basic-Networking/content.md: -------------------------------------------------------------------------------- 1 |

计算机网络基础知识

2 | 3 |
Abstract: 理解基本术语,浅析网络协议
4 | 5 | ## 基本术语 6 | 7 | ###### 值得注意的是:在本篇中所涉及的计算机网络基本术语多为宏观上支撑我们接下来学习所必须了解的(如性能指标,基本网络的分类、交换方式等,协议的基本概念等),不包括具体参考模型的协议层中特有的知识点,这些内容将在之后的TCP/IP Reference Model章节对应部分进行解读。如不需要基础知识点的入门指引,可直接移步下一章的学习内容~ 8 | 9 | ##### 描述网络性能指标: 10 | 11 | 1. 速率:连接在计算机网络上的主机在数字信道上传送数据的速率,也称为数据率或比特率。 12 | 13 | 2. 带宽:网络的通信线路传送数据的能力。即:在单位时间内从网络中的某一个点到另一点所能通过的“最高数据率”。 14 | 15 | 3. 吞吐量:在单位时间内通过某个网络(信道/接口)的数据量。 16 | 17 | 4. 时延:数据(一个报文或分组,甚至比特)从网络(或链路)的一端传送到另一端所需的时间。 18 | 19 | 发送时延:主机或路由器发送数据帧所需要的时间,即:从发送数据帧的第一个比特算起到该帧的最后一个比特发送完毕所需要的时间。 20 | 21 | 传播时延:电磁波在信道中传播一定的距离需要花费的时间。 22 | 23 | 处理时延:主机或路由器在收到分组时要花费一定的时间进行处理。例如:分析分组的首部信息、从分组中提取数据部分、进行差错检验或查找适当的路由等等。 24 | 25 | 排队时延:分组在经过网络传输时,要经过许多路由器。但分组在进入路由器后要先在输入队列中排队等待处理。 26 | 27 | *注意:对于高速网络链路,我们提高的仅仅是数据的发送速率而不是比特在链路上的传播速率而不是比特在链路上的传播速率。* 28 | 29 | 5. 时延带宽积:传播时延*带宽 30 | 6. 往返时间RTT:从发送方发送数据开始,到发送方收到来自接收方的确认总共经历的时间。 31 | 7. 利用率:信道利用率和网络利用率两种 32 | 33 | ##### 网络分类: 34 | 35 | 1. 按交换技术: 36 | 电路交换网络 | 报文交换网络 |分组交换网络 | 混合交换网络 37 | 38 | 2. 按通信传输方式: 39 | 广播型网络 | 点到点传播网络 40 | 41 | 3. 按网络拓扑结构: 42 | 星型网络 | 环型网络 | 总线网 | 树型网 | 网状型网 43 | 44 | 4. 按距离尺度: 45 | 46 | 个域网(PAN)|局域网(LAN)|城域网(MAN)| 广域网(WAN)|网际网(Internet) 47 | 48 | 5. 按传输介质: 49 | 50 | 有线网 | 无线网 51 | 52 | ##### 交换方式: 53 | 54 | 1. 电路交换 55 | 公共电话网(PSTN网)和移动网(包括GSM和CDMA网)采用的都是电路交换技术,它的基本特点是采用面向连接的方式,在双方进行通信之前,需要为通信双方分配一条具有固定宽带的通信电路,通信双方在通信过程中一直占用所分配的资源,直到通信结束,并且在电路的建立和释放过程中都需要利用相关的信令协议。这种方式的优点是在通信过程中可以保证为用户提供足够的带宽,并且实时性强,时延小,交换设备成本低,但同时带来的缺点是信道利用率低,一旦电路被建立不管通信双方是否处于通话状态分配的电路一直被占用。 56 | 57 | 2. 报文交换 58 | 报文交换是以报文为数据交换的单位,报文携带有目标地址、源地址等信息,在交换结点采用存储转发的传输方式;由于报文长度差异很大,长报文可能导致很大的时延;为了满足各种长度报文的需要并且达到高效的目的,节点需要分配不同大小的缓冲区,否则就有可能造成数据传送的失败,这样对每个节点来说缓冲区的分配也比较困难。另外一个缺点是出错时,整个报文都将重传。 59 | 60 | 3. 分组交换 61 | 分组交换仍采用存储转发传输方式,但将一个长报文先分割为若干个较短的分组,然后把这些分组(携带源、目的地址和编号信息)逐个地发送出去。采用分组交换技术,在通信之前不需要建立连接,每个节点首先将前一节点送来的分组收下并保存在缓冲区中,然后根据分组头部中的地址信息选择适当的链路将其发送至下一个节点,这样在通信过程中可以根据用户的要求和网络的能力来动态分配带宽。分组交换比电路交换的信道利用率高,但时延较大。分组转发的带来的问题:排队时延以及增加头部带来的开销。 62 | 63 | 64 | 65 | ## 网络协议 66 | 67 | ###### 协议是对等实体之间的通信规程,主要对实体间通信的语法、语义、同步、纠错四大方面进行约定。 68 | 69 | ![Protocol-Hierarchies](https://github.com/zhyChesterCheung/GoGetit/blob/main/image/Networking/Basic-Networking/Protocol-Hierarchies.jpeg) 70 | 71 | ##### 协议的层次结构: 72 | 73 | 要理解网络协议的内涵,我们还需要知道这里所提**对等实体**,即通信双方对应层的实体,即Sender的物理层对应Receiver的物理层,相同主机层与层之间只通过**服务与接口**通信,而不同主机间只有对应的层次能够感知到双方的通信。 74 | 75 | **服务**是各层向它**上层**提供的一组原语。服务定义了两层间的接口,**上层是服务用户向,下层是服务提供者**。 76 | 77 | 只要不改变提供给用户的服务,实体可以任意的改变他们的协议。 78 | 79 | 由上我们也可以明白,之所以将网络协议进行层次的划分,是为了降低在网络设计时的复杂程度,简化了层与层间的数据交换,类比Java等面向对象的语言,这种封装与独立的思想其实并不难理解。 80 | 81 | ##### 协议层的设计问题 82 | 83 | 理解了为什么我们将网络通信通过协议垂直划分为多个层次,那么我们还需要做知道一些在写一设计时需要考虑的质量因素,包括: 84 | 85 | | 质量指标 | 解释 | 86 | | :------: | :----------------------------------------------------------: | 87 | | 可靠性 | 保证网络正常运行的设计问题 | 88 | | 可扩展性 | 体现网络互联的需求 | 89 | | 资源分配 | 包括带宽分配、流量控制、服务质量(吞吐量、延时) | 90 | | 安全性 | 用于抵抗不同的网络威胁,通常通过以下几点来实现,如保密性、认证、完整性等 | 91 | 92 | ##### 面向连接/无连接服务 93 | 94 | ##### 协议与服务的关系 -------------------------------------------------------------------------------- /docs/Networking/README.md: -------------------------------------------------------------------------------- 1 |

Go Based Networking

2 | 3 |

前序

4 | 5 | - 本目录下的内容以介绍Go语言网络通信技术为主,辅以介绍基本的计算机网络知识。同时,在重要章节将refer一些常见代码demo或进阶项目实例以加深对相关网络技术的阐释。 6 | - 为了不引起争议,目录将以英文为主,翻译供辅助参考。 7 | - 我们会持续改进,增加更多丰富的内容,对于已有的内容,如果您有疑问或者改进的建议,可以通过 issues 与我们联系,当然我们欢迎你通过 PR 参与到项目的共建中来。 8 | 9 | 10 | 11 | ## [一、网络基础知识](./Basic-Networking/content.md) 12 | 13 | #### 1、基本术语 14 | 15 | #### 2、Protocol Hierarchies 网络协议 16 | 17 | - 协议的层次结构 18 | - 协议层的设计问题 19 | - 面向连接/无连接服务 20 | - 服务和协议的关系 21 | 22 | 23 | 24 | ## 二、TCP/IP Reference Model 25 | 26 | #### 1、Physical Layer 物理层 27 | 28 | - 基本术语 29 | - 信道公式 30 | - 数组调制与多路复用 31 | - 交换技术 32 | 33 | 34 | 35 | #### 2、Data Link Layer 数据链路层 36 | 37 | - 基本功能与实现 38 | - 帧的定界 39 | - 差错控制 40 | - 检纠错问题 41 | - 基本协议 42 | - 以太网 43 | - 无线网络 44 | 45 | #### 3、Network Layer 网络层 46 | 47 | - 虚电路与数据报 48 | - 路由算法 49 | - Internet的网络层 50 | - IPV4 51 | - IPV6 52 | - 路由转发过程 53 | - 网络控制协议 54 | 55 | #### 4、Transport Layer 传输层 56 | 57 | - 传输层服务 58 | Socket 套接字 59 | 60 | - 传层协议详解 61 | 62 | TCP/IP连接建立与断开过程(三次握手、四次挥手) 63 | 64 | - TCP和UDP协议 65 | 66 | - 拥塞控制 67 | 68 | #### 5、Application Layer 应用层 69 | 70 | - 网络应用与协议 71 | - C/S、B/S模式 72 | - HTTP协议 73 | 74 | 75 | 76 | ## 三、Go-HTTP 77 | 78 | #### 1、HTTP服务 79 | 80 | #### 2、Go标准库实现HTTP 81 | 82 | #### 3、net/http库源码详解 83 | 84 | #### 4、第三方库介绍(例:Req) 85 | 86 | 87 | 88 | ## 四、Go-RPC 89 | 90 | #### 1、RPC简介及原理架构 91 | 92 | #### 2、Go标准库实现RPC 93 | 94 | #### 3、Go语言的RPC框架(例:GRPC) 95 | 96 | -------------------------------------------------------------------------------- /docs/Operating-System/Direct-Execution/Content.md: -------------------------------------------------------------------------------- 1 |

CPU 直接执行

2 |
Abstract: 从虚拟化 CPU 中发现了一些存在的挑战,在直接执行的基础上分析问题所在最终提出受限直接执行
3 | 4 | ## 虚拟化 CPU 存在的问题 5 | 6 | [进程与进程相关的 API](./Process-and-process-Api/Content.md) 一文中我们介绍了操作系统通过分时机制虚拟化 CPU,表面意义上实现了并发,但是其实现是存在一些问题的: 7 | 8 | - **性能:** 如何在不增加系统开销的情况下实现虚拟化? 9 | - **控制:** 如何在保持对 CPU 控制的同时有效地运行进程? 10 | 11 | - 控制对于操作系统来说尤为重要,它意味着资源的将如何利用,如果操作系统无法做到绝对管控,那么就会出现进程无限期地 Run 下去,甚至随意访问不应该被允许访问的数据这样一些现象。 12 | 13 | > 绝对管控的同时保持高性能是构建操作系统真正意义上的挑战。通常需要依赖硬件的支持。 14 | 15 | ## 直接执行 16 | 17 | 为了实现我们的预期,构建操作系统的人提出了一种技术并称之为受限直接执行。**直接执行**,顾名思义,直接在 CPU 上运行程序。 18 | 19 | 所以,当操作系统要运行一段程序的时候,就会 20 | 1. 在进程列表中创建一个新的进程; 21 | 2. 为其分配一段内存; 22 | 3. 将程序代码从磁盘加载到内存中; 23 | 4. 通过 argc/argv 设置堆栈 24 | 5. 清除寄存器 25 | 6. 定位程序的入口(类似于 main()) 26 | 7. 跳到入口处并开始运行 27 | 8. 释放进程内存 28 | 9. 将该进程从进程列表中移除 29 | 30 | 这便是直接执行的过程,简单也存在很多问题 31 | - 在程序运行过程中,操作系统怎么保证程序在管控范围内运行,简单来说,就是怎么知道这段程序是否做了我们不期望做的事 32 | - 当我们运行一个进程时,操作系统怎么让它停下来并且切换到另一个线程继续运行,怎么实现我们期望的虚拟化 CPU 33 | 34 | > 下面我会多方面去解释为什么在 **"直接执行"** 之后出现了 **"受限直接执行"**,从而更好地理解虚拟化 CPU 到底需要什么? 35 | 36 | ### 限制操作 37 | 38 | > 因为程序本身直接在硬件 CPU 上运行,所有很直观的,直接执行具有高效的优势,可以很快实现用户的需求。但就像上面说的,如果在执行过程中我们希望做一些限制操作,例如发起资源访问,或者发起磁盘 I/O。 39 | 40 | **Q:** 41 | 42 | 进程必须能够执行 I/O 和其他一些受限操作,但不能让进程完全管控系统。前面的挑战部分我们提到硬件的支持,那么操作系统和硬件如何协同工作才能做到这一点? 43 | 44 | **A:** 45 | 46 | - 最简单的方式就是:万能进程,让所有进程都可以执行 I/O 和任何受限操作,但是这就意味着没有权限可言。 47 | 48 | - 现代操作系统引入新的处理器模式来解决这个问题。 49 | - **用户模式:** 在用户模式下运行的代码,其功能会受到限制。e.g.:用户态下进程无法发起 I/O 请求; 50 | - **内核模式:** 内核模式下,运行的代码权限不受限制,可以执行任何特权操作 51 | 52 | **Q:** 53 | 54 | 在这种解决方案之下仍然存在着问题,如果用户进程需要执行一些特权操作,就不可行了。 55 | 56 | **A:** 57 | 58 | 几乎所有的现代硬件都为用户模式下的程序提供了执行系统调用的能力,像 Atlas 一些老的机器上,系统调用针对关键功能向用户程序提供允许。大多数的操作系统会提供数百个调用。(可以参考 POSIX 标准) 59 | 60 | - 执行一次系统调用,程序必须执行 trap 指令,跳转到内核并且同时将特权级别提升到内核模式,一旦进入内核,系统就可以执行所需的任何特权操作,从而为调用进程完成所需的工作。完成后,操作系统调用一个特殊的 return-from-trap指令,返回到调用用户程序,同时将特权级别降低到用户模式。 61 | 62 | - 硬件在执行 trap 指令的时候需要注意保证有足够多的调用寄存器,因为当 return-from-trap 指令执行时,需要正确返回。像在 x86 上,处理器讲程序计数器、标志位、其他寄存器推入每个进程的内核堆栈,当 return-from-trap 指令执行的时候,需要将堆栈中的内容按照顺序弹出,并且恢复执行用户模式下的程序。 63 | 64 | **Q:** 65 | 66 | 那么 trap 指令时如何知道哪些代码应该在操作系统中运行呢? 67 | 68 | **A:** 69 | > 显然调用进程无法指定要跳转到的地址。kernel 这个时候就起到了控制的作用。 70 | 71 | kernel 通过在启动时设置 trap 表来实现,机器启动的时候,它以内核模式启动,根据需要自由配置机器硬件,操作系统做的第一件事便是告诉硬件当某些异常发生时要运行什么代码,硬件会记忆处理程序的位置,直到机器下次重新启动。 72 | 73 | 为了指定准确的系统调用,通常情况下会为每个系统调用分配一个系统调用编码。用户模式下的代码负责将所需要的系统调用编码放置在寄存器中或堆栈上的指定位置。操作系统在处理 trap 堆栈中的系统调用时会检查此编号,如果有效则执行相应的代码,这种间接级别是一种保护形式。用户代码不能指定要跳转的确切地址,而是通过编号请求特定的服务。 74 | 75 | 我们假设每个进程都有一个内核堆栈,当进入和离开内核时,寄存器(包括通用寄存器和程序计数器)在其中由硬件进行保存和恢复。 76 | 77 | 受限直接执行(LDE)协议有两个阶段,第一次在启动时,内核初始化 trap 表,CPU 记录位置以供后续使用。内核通过特权指令实现。 78 | 79 | 第二次在运行进程时,内核在使用 return-from-trap 指令开始执行进程执行设置例如分配内存等的一些事情时,会将 CPU 切换到用户模式下并开始运行该进程。当进程希望发出系统调用时,会通过 trap back 转回操作系统,操作系统处理完成再通过 trap 将控制权交给进程,然后进程完成任务,并从 main() 返回,通过到这儿就到了退出程序中,操作系统清理完毕。 80 | 81 | ### 进程切换 82 | 83 | 在进程间切换应该很简单吧,操作系统只需要负责停止一个进程的运行并切换到另一个进程,如果你这么想就大错特错了。当一个进程正在 CPU 上运行,这就意味着操作系统并没有运行,如果操作操作系统没有运行,不在 CPU 上运行,它显然无法做任何操作。 84 | 85 | 那么操作系统如何获取对 CPU 的控制,以便可以在进程之间切换。 86 | 87 | #### 协作方式:等待系统调用 88 | 89 | > 在一些古老的操作系统中是通过协作方式切换进程,操作系统信任系统进程的合理行为。 90 | 91 | 大多数进程通过进行系统调用来频繁地将 CPU 的控制权转交给操作系统,这种系统通常只包括一个显式 yield 系统调用,只负责将控制权转移到操作系统以便可以运行其他进程。应用程序在执行非法操作时也会将控制权转移给操作系统。在协作调度系统中,操作系统通过等待系统调用或某种非法操作的发生来重新获得对 CPU 的控制。 92 | 93 | #### 非协作方式:操作系统控制 94 | 95 | > 如果没有硬件的额外协助,当进程拒绝进行系统调用并将控制权转交给操作系统时,操作系统无法完成很多任务,在协作方式下,当一个进程陷入无限循环时,唯一的办法是求助于重启。 96 | 97 | 那么不使用协作方式,操作系统如何去获取对 CPU 的控制?而且操作系统如何保证流氓进程不接管机器呢? 98 | 99 | 答案就是**定时器中断**,定时器设备可以被编程从而每隔几毫秒产生一次中断,当中断引发时,当前运行的进程被暂停,并且操作系统中预先配置的中断护理程序运行。此时,操作系统获得了对 CPU 的控制,因此实现了停止当前的进程并启动另一个新的进程。 100 | 101 | 类似于系统调用,当定时器中断时,操作系统必须通知硬件运行哪个代码,所以在启动时操作系统正是如何做的,其次,在引导操作期间,一旦计时器开始,操作系统就处于安全状态,因为控制最终会返回给操作系统,操作系统可以自由地运行用户程序,定时器也可以被关闭。 102 | 103 | 当中断发生的时候,硬件要保存足够的中断发生时正在运行的程序的状态。以便后续执行返回指令的时候能够正确地恢复正在运行的程序。和显式系统调用很相似,不是吗🤔️。 104 | 105 | #### 上下文传递 106 | 107 | > 既然操作系统已经重新获得控制权,无论是通过系统调用的协作方式还是定时器中断的方式。是继续运行当前正在运行的进程,还是切换到不同的进程这个决定交由 CPU 调度来决定。 108 | 109 | 如果决定进行切换,操作系统就会执行一段上下文切换的底层代码。通过这种方式,操作系统因此可以确保当最终执行 return-from-trap 指令时,系统能够正确地恢复另一个进程的执行。 110 | 111 | --- 112 | 113 | **上下文切换:** 操作系统为当前正在执行的进程保存一些寄存器值到其内核堆栈中,并为即将执行的进程从其内核堆栈中恢复一些寄存器值。 114 | 115 | --- 116 | 117 | 为了保存当前运行进程的上下文,操作系统将执行一些底层汇编代码,来保存当前运行进程的通用寄存器 PC 和内核堆栈指针,然后恢复寄存器 PC,并切换到即将执行的进程的内核堆栈。通过切换堆栈,内核在被中断的进程的上下文中进入对代码的调用,并在即将执行的进程的上下文中返回。当操作系统最终执行一个从 trap 返回的指令时,即将执行的进程便成为了当前运行的进程,完成了上下文切换。 118 | 119 | 在此期间会发生两种类型的寄存器保存/恢复。 120 | 1. **定时器中断发生时**;在这种情况下,正在运行的进程的用户寄存器由硬件隐式保存,使用该进程的内核堆栈。 121 | 2. **操作系统决定从 A 切换到 B 时**;在这种情况下,内核寄存器由操作系统显式保存,但这次保存在进程的的内存中。 122 | 123 | ### 并发 124 | 125 | > 在系统调用期间如果发生了计时器中断,会发生什么呢?或者当处理一个中断而另一个中断发生的时候会发生什么呢? 126 | 127 | 这在内核中处理时非常难的,有两种方案,操作系统可能在中断处理期间禁用中断;当然后来构建操作系统的人开发了许多复杂的锁的方案保护对内部数据结构的并发访问。这部分后续内容中会持续补充。 128 | 129 | ## 总结 130 | 131 | 在我们发现了虚拟化 CPU 存在的一些挑战之后,我们通过限制操作,进程切换,并发这样一些关键的底层机制来实现了真正意义上的 CPU 虚拟化。这便称作**受限直接执行**。只需要在 CPU 上运行您想要运行的程序,前提是需要确保设置硬件以限制进程在没有操作系统帮助的情况下可以找行的操作。 -------------------------------------------------------------------------------- /docs/Operating-System/Process-and-process-Api/Content.md: -------------------------------------------------------------------------------- 1 | 2 |

进程与进程相关的API

3 |
Abstract: 站在上层分析操作系统,分析进程,再不断深入了解其结构,熟悉其性质
4 | 5 | ## 从上层看操作系统和进程 6 | 7 | 操作系统通过虚拟化CPU的方式创造了一种多CPU的错觉:运行一个进程,终止然后运行另一个进程,这称作**CPU分时机制**,也正是为什么我们可以在操作系统上运行任意数量的并发进程。 8 | 9 | 在许多操作系统中,**常见的设计范式**是将高级策略从低级机制中抽离出来。机制即系统的运行方式,策略即与用户直接对接的解决方案。 10 | - 操作系统直接面向用户的便是策略 11 | - 运行中的程序可以抽象地认为是进程 12 | - 策略决定了操作系统某一时刻运行哪个(或哪些)进程。 13 | 14 | ## 重新定义进程 15 | 16 | 我们可以根据执行过程中访问或影响的系统的不同部分来认知一个进程 17 | - 任何时间内,包含进程状态的组件是它的内存,指令位于内存中;正在运行的程序读取和写入的数据也位于内存中。 18 | - 因此,进程可以寻址的内存(称为其地址空间)是进程的一部分。进程状态的一部分是寄存器;许多指令显式读取或更新寄存器。 19 | 20 |
21 |
22 |
23 | 进程组成结构 24 |
25 |
26 |
27 | 28 | 在这其中 PCB 是进程存在的唯一标志,有了大致的这样一个理解之后,我们可以重新定义一下进程: 29 | 30 | 进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位 31 | 1. 从结构上看,每个进程都有程序段、数据段和进程控制段三个部分,所以进程具有**结构性** 32 | 2. 最前面在讲分时机制的时候,我们认识到了进程各自独立,也相互制约并且同时运行,所以进程具有**独立性、异步性、并发性** 33 | 3. 运行和终止说的是进程的生命周期,那么也说明了进程的**动态性** 34 | 35 | ## 进程是如何被创建的 36 | 我们需要思考,操作系统是如何启动并运行程序的,程序是如何转换为进程的? 37 | 38 |
39 |
40 |
41 | 进程创建内容 42 |
43 |
44 |
45 | 进程创建流程 46 |
47 |
48 |
49 | 50 | 这里我画了一张图给大家,过程以及加载传递的内容非常详细,但其中有一个需要注意的点就是: 51 | - 简单的操作系统,或者说原始的操作系统,对于将代码和静态数据加载到内存中这一操作是一次性完成的 52 | - 现代操作系统大多通过懒加载的方式,程序执行期间才会加载,这一过程可以在后续的分页和交换机制内容中得到一个详细的解释 53 | 54 | ## 进程的状态 55 | > 在上述的流程中,进程被创建,那么在给定的时间段内,进程所处的状态是何种情况呢? 56 | 57 | 简单来说,进程有**运行**、 **就绪**、**阻塞**三种状态: 58 | - 进程正在处理机上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。 59 | - 进程已处于准备运行的状态,即进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行。 60 | - 又称等待状态:进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。 61 | 62 |
63 |
64 |
65 | 进程状态转换 66 |
67 |
68 |
69 | 70 | ## 进程相关API 71 | > 这里所介绍的 API 应用在各大现代操作系统中 72 | 73 | 1. 创建 74 | - 所有的操作系统均包含有创建新进程的方法,我们通过shell命令或者GUI操作打开新的应用程序,都是操作系统在调用创建的 API 在创建新的进程 75 | 2. 销毁 76 | - 当然通常情况下进程在运行完成时会自动销毁,但是操作系统还提供了销毁进程的 API,在 Windows 系统上结束进程或者在 Mac 系统上强制退出都属于这种调用 77 | 3. 等待 78 | - 举个例子,有时候我们有一种诉求,希望等待某个进程执行完成之后再执行新的进程,操作系统便提供了进程等待的 API 79 | 4. 其他控制方式 80 | - 为满足用户需求,除了上述三种控制还需要别的一些控制方式,例如大多数的操作系统都可以暂停正在执行的进程,也可以恢复暂停中的进程 81 | 5. 状态 82 | - 通常还有一些接口来获取进程的一些状态信息,例如运行了多长时间,或者正处于什么状态 83 | 84 | ## 策略层-操作系统如何管理 85 | > 和我们所看到的程序一样,操作系统也是一种特殊的程序,它也会通过一些数据结构来跟踪各种各样的信息 86 | - 为了跟踪每个进程的状态,操作系统可能会保留所有处于就绪状态的进程列表以及一些附加信息来跟踪当前正在运行的进程 87 | - 当 I/O 事件结束时,操作系统应确保唤醒正确的进程并准备好再次运行 88 | - 对于终止的进程,寄存器上下文将保存其寄存器的内容。当一个进程终止后,它的寄存器会被保存到这个内存位置;通过恢复这些寄存器,操作系统可以恢复运行该进程。 -------------------------------------------------------------------------------- /docs/Operating-System/Process-and-process-Api/img/process_createtion_procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Operating-System/Process-and-process-Api/img/process_createtion_procedure.png -------------------------------------------------------------------------------- /docs/Operating-System/Process-and-process-Api/img/process_creation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Operating-System/Process-and-process-Api/img/process_creation.png -------------------------------------------------------------------------------- /docs/Operating-System/Process-and-process-Api/img/process_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Operating-System/Process-and-process-Api/img/process_status.png -------------------------------------------------------------------------------- /docs/Operating-System/Process-and-process-Api/img/process_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/docs/Operating-System/Process-and-process-Api/img/process_structure.png -------------------------------------------------------------------------------- /docs/Operating-System/README.md: -------------------------------------------------------------------------------- 1 | # 操作系统 2 | 3 | ## 前序 4 | - 结合安全性、性能和复杂性三个因素的考虑,用 Go 语言编写 Kernel 并不占有优势。 5 | - 此目录中的内容将以介绍操作系统基本概念为主,针对特定章节的内容会有相关 Go 语言实践 6 | - 为了不引起争议,目录将以英文为主,翻译供辅助参考 7 | - 我们会持续改进,增加更多丰富的内容,对于已有的内容,如果您有疑问或者改进的建议,可以通过 issues 与我们联系,当然我们欢迎你通过 PR 参与到项目的共建中来。 8 | 9 | ## Virtualization [虚拟化] 10 | 11 | 1. [Process and process API [进程与进程相关的API]](./Process-and-process-Api/Content.md) 12 | 13 | 14 | 2. [Direct Execution [CPU 直接执行]](./Direct-Execution/Content.md) 15 | - 直接执行 16 | - 受限直接执行 17 | 18 | 3. Scheduling [调度] 19 | - 单 CPU 调度 20 | - 多 CPU 调度 21 | - 彩票调度 22 | 23 | 4. Address-related [地址相关] 24 | - 地址空间 25 | - 地址转换 26 | 27 | 5. Space-related [空间相关] 28 | - 内存及内存管理分段技术 29 | - 自由空间管理 30 | 31 | 6. Complete VM System [虚拟系统] 32 | 33 | ## Concurrency 并发性 34 | 35 | 1. Concurrency and Threads [并发和线程] 36 | 37 | 2. Locks [锁] 38 | 39 | 3. Condition Variables and Semaphores [条件变量和信号量] 40 | 41 | ## Persistence 持久性 42 | 43 | 1. File System and devices [文件系统和硬件设备] 44 | - 输入/输出设备 45 | - 硬盘驱动 46 | - 普通文件系统及实现,FFS,LFS,NFS,AFS 47 | 48 | 2. Flash-based SSDs [闪存 SSD] 49 | 50 | 3. Distributed Systems [分布式系统] 51 | 52 | ## 引用 53 | - [The benefits and costs of writing a POSIX kernel in a high-level language](https://www.usenix.org/system/files/osdi18-cutler.pdf) -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module GoGetit 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /image/Algorithm/advanced/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Algorithm/advanced/.gitkeep -------------------------------------------------------------------------------- /image/Algorithm/basic/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Algorithm/basic/.gitkeep -------------------------------------------------------------------------------- /image/Algorithm/basic/00-complexion-analysis/01-result-of-test.go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Algorithm/basic/00-complexion-analysis/01-result-of-test.go.png -------------------------------------------------------------------------------- /image/Algorithm/basic/01-recursion/01-fibonacci.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Algorithm/basic/01-recursion/01-fibonacci.jpg -------------------------------------------------------------------------------- /image/Algorithm/basic/09-backtracking/8queens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Algorithm/basic/09-backtracking/8queens.png -------------------------------------------------------------------------------- /image/Networking/Basic-Networking/Protocol-Hierarchies.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/Networking/Basic-Networking/Protocol-Hierarchies.jpeg -------------------------------------------------------------------------------- /image/douLinkedlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/douLinkedlist.png -------------------------------------------------------------------------------- /image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/logo.png -------------------------------------------------------------------------------- /image/maxheap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/maxheap.png -------------------------------------------------------------------------------- /image/minheap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/minheap.png -------------------------------------------------------------------------------- /image/queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/queue.png -------------------------------------------------------------------------------- /image/singLinkedList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/singLinkedList.png -------------------------------------------------------------------------------- /image/singleRotaLeft1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/singleRotaLeft1.png -------------------------------------------------------------------------------- /image/singleRotaLeft2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/singleRotaLeft2.png -------------------------------------------------------------------------------- /image/singleRotaRight1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/singleRotaRight1.png -------------------------------------------------------------------------------- /image/singleRotaRight2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/singleRotaRight2.png -------------------------------------------------------------------------------- /image/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/stack.png -------------------------------------------------------------------------------- /image/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superego-CodeEngineer/GoGetit/9823ea9a11e6511463a1101da511f084243429e3/image/tree.png --------------------------------------------------------------------------------