Update README

This commit is contained in:
krahets 2022-11-23 15:50:59 +08:00
parent 550024f69b
commit faba5af781
19 changed files with 61 additions and 57 deletions

View file

@ -1,5 +1,5 @@
<p align="center">
<a href="https://krahets.github.io/hello-algo/">
<a href="https://www.hello-algo.com/">
<img src="docs/index.assets/conceptual_rendering.png" width="220">
</a>
</p>
@ -12,17 +12,11 @@
动画图解、能运行、可讨论的</br>数据结构与算法快速入门教程
</p>
<p align="center">
<a href="https://krahets.github.io/hello-algo/">
<img src="docs/index.assets/demo.png" width="700">
</a>
</p>
<p align="center">
<em>
前往阅读 -
<a href="https://hello-algo.pages.dev/">
hello-algo.pages.dev
前往阅读 >>
<a href="https://www.hello-algo.com/">
hello-algo.com
</a>
</em>
</p>

View file

@ -8,7 +8,7 @@ comments: true
![array_definition](array.assets/array_definition.png)
<p style="text-align:center"> Fig. 数组定义与存储方式 </p>
<p align="center"> Fig. 数组定义与存储方式 </p>
!!! note
@ -42,7 +42,7 @@ comments: true
![array_memory_location_calculation](array.assets/array_memory_location_calculation.png)
<p style="text-align:center"> Fig. 数组元素的内存地址计算 </p>
<p align="center"> Fig. 数组元素的内存地址计算 </p>
```java title=""
// 元素内存地址 = 数组内存地址 + 元素长度 * 元素索引
@ -117,7 +117,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
![array_insert_remove_element](array.assets/array_insert_remove_element.png)
<p style="text-align:center"> Fig. 在数组中插入与删除元素 </p>
<p align="center"> Fig. 在数组中插入与删除元素 </p>
=== "Java"

View file

@ -14,7 +14,7 @@ comments: true
![linkedlist_definition](linked_list.assets/linkedlist_definition.png)
<p style="text-align:center"> Fig. 链表定义与存储方式 </p>
<p align="center"> Fig. 链表定义与存储方式 </p>
=== "Java"
@ -86,7 +86,7 @@ comments: true
![linkedlist_insert_remove_node](linked_list.assets/linkedlist_insert_remove_node.png)
<p style="text-align:center"> Fig. 在链表中插入与删除结点 </p>
<p align="center"> Fig. 在链表中插入与删除结点 </p>
=== "Java"
@ -219,4 +219,4 @@ comments: true
![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png)
<p style="text-align:center"> Fig. 常见链表类型 </p>
<p align="center"> Fig. 常见链表类型 </p>

View file

@ -11,7 +11,7 @@ comments: true
## 数组 VS 链表
<p style="text-align:center"> Table. 数组与链表特点对比 </p>
<p align="center"> Table. 数组与链表特点对比 </p>
<div class="center-table" markdown>
@ -28,7 +28,7 @@ comments: true
「缓存局部性Cache locality」涉及到了计算机操作系统在本书不做展开介绍建议有兴趣的同学 Google / Baidu 一下。
<p style="text-align:center"> Table. 数组与链表操作时间复杂度 </p>
<p align="center"> Table. 数组与链表操作时间复杂度 </p>
<div class="center-table" markdown>

View file

@ -26,7 +26,7 @@ comments: true
![space_types](space_complexity.assets/space_types.png)
<p style="text-align:center"> Fig. 算法使用的相关空间 </p>
<p align="center"> Fig. 算法使用的相关空间 </p>
=== "Java"
@ -144,7 +144,7 @@ $$
![space_complexity_common_types](space_complexity.assets/space_complexity_common_types.png)
<p style="text-align:center"> Fig. 空间复杂度的常见类型 </p>
<p align="center"> Fig. 空间复杂度的常见类型 </p>
!!! tip
@ -252,7 +252,7 @@ $$
![space_complexity_recursive_linear](space_complexity.assets/space_complexity_recursive_linear.png)
<p style="text-align:center"> Fig. 递归函数产生的线性阶空间复杂度 </p>
<p align="center"> Fig. 递归函数产生的线性阶空间复杂度 </p>
### 平方阶 $O(n^2)$
@ -317,7 +317,7 @@ $$
![space_complexity_recursive_quadratic](space_complexity.assets/space_complexity_recursive_quadratic.png)
<p style="text-align:center"> Fig. 递归函数产生的平方阶空间复杂度 </p>
<p align="center"> Fig. 递归函数产生的平方阶空间复杂度 </p>
### 指数阶 $O(2^n)$
@ -350,7 +350,7 @@ $$
![space_complexity_exponential](space_complexity.assets/space_complexity_exponential.png)
<p style="text-align:center"> Fig. 满二叉树下的指数阶空间复杂度 </p>
<p align="center"> Fig. 满二叉树下的指数阶空间复杂度 </p>
### 对数阶 $O(\log n)$

View file

@ -92,7 +92,7 @@ $$
![time_complexity_first_example](time_complexity.assets/time_complexity_first_example.png)
<p style="text-align:center"> Fig. 算法 A, B, C 的时间增长趋势 </p>
<p align="center"> Fig. 算法 A, B, C 的时间增长趋势 </p>
相比直接统计算法运行时间,时间复杂度分析的做法有什么好处呢?以及有什么不足?
@ -153,7 +153,7 @@ $T(n)$ 是个一次函数,说明时间增长趋势是线性的,因此易得
![asymptotic_upper_bound](time_complexity.assets/asymptotic_upper_bound.png)
<p style="text-align:center"> Fig. 函数的渐进上界 </p>
<p align="center"> Fig. 函数的渐进上界 </p>
本质上看,计算渐进上界就是在找一个函数 $f(n)$ **使得在 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别(仅相差一个常数项 $c$ 的倍数)**。
@ -245,7 +245,7 @@ $$
![time_complexity_common_types](time_complexity.assets/time_complexity_common_types.png)
<p style="text-align:center"> Fig. 时间复杂度的常见类型 </p>
<p align="center"> Fig. 时间复杂度的常见类型 </p>
!!! tip
@ -375,7 +375,7 @@ $$
![time_complexity_constant_linear_quadratic](time_complexity.assets/time_complexity_constant_linear_quadratic.png)
<p style="text-align:center"> Fig. 常数阶、线性阶、平方阶的时间复杂度 </p>
<p align="center"> Fig. 常数阶、线性阶、平方阶的时间复杂度 </p>
以「冒泡排序」为例,外层循环 $n - 1$ 次,内层循环 $n-1, n-2, \cdots, 2, 1$ 次,平均为 $\frac{n}{2}$ 次,因此时间复杂度为 $O(n^2)$ 。
@ -454,7 +454,7 @@ $$
![time_complexity_exponential](time_complexity.assets/time_complexity_exponential.png)
<p style="text-align:center"> Fig. 指数阶的时间复杂度 </p>
<p align="center"> Fig. 指数阶的时间复杂度 </p>
在实际算法中,指数阶常出现于递归函数。例如以下代码,不断地一分为二,分裂 $n$ 次后停止。
@ -516,7 +516,7 @@ $$
![time_complexity_logarithmic](time_complexity.assets/time_complexity_logarithmic.png)
<p style="text-align:center"> Fig. 对数阶的时间复杂度 </p>
<p align="center"> Fig. 对数阶的时间复杂度 </p>
与指数阶类似,对数阶也常出现于递归函数。以下代码形成了一个高度为 $\log_2 n$ 的递归树。
@ -577,7 +577,7 @@ $$
![time_complexity_logarithmic_linear](time_complexity.assets/time_complexity_logarithmic_linear.png)
<p style="text-align:center"> Fig. 线性对数阶的时间复杂度 </p>
<p align="center"> Fig. 线性对数阶的时间复杂度 </p>
### 阶乘阶 $O(n!)$
@ -618,7 +618,7 @@ $$
![time_complexity_factorial](time_complexity.assets/time_complexity_factorial.png)
<p style="text-align:center"> Fig. 阶乘阶的时间复杂度 </p>
<p align="center"> Fig. 阶乘阶的时间复杂度 </p>
## 最差、最佳、平均时间复杂度

View file

@ -17,7 +17,7 @@ comments: true
![classification_logic_structure](classification_of_data_strcuture.assets/classification_logic_structure.png)
<p style="text-align:center"> Fig. 线性与非线性数据结构 </p>
<p align="center"> Fig. 线性与非线性数据结构 </p>
## 物理结构:连续与离散
@ -29,7 +29,7 @@ comments: true
![classification_phisical_structure](classification_of_data_strcuture.assets/classification_phisical_structure.png)
<p style="text-align:center"> Fig. 连续空间存储与离散空间存储 </p>
<p align="center"> Fig. 连续空间存储与离散空间存储 </p>
**所有数据结构都是基于数组、或链表、或两者组合实现的。** 例如栈和队列,既可以使用数组实现、也可以使用链表实现,而例如哈希表,其实现同时包含了数组和链表。

View file

@ -19,7 +19,7 @@ comments: true
1 字节 (byte) = 8 比特 (bit) 1 比特即最基本的 1 个二进制位
<p style="text-align:center"> Table. Java 的基本数据类型 </p>
<p align="center"> Table. Java 的基本数据类型 </p>
<div class="center-table" markdown>
@ -57,7 +57,7 @@ comments: true
=== "C++"
```cpp title=""
```
=== "Python"
@ -76,6 +76,6 @@ comments: true
![computer_memory_location](data_and_memory.assets/computer_memory_location.png)
<p style="text-align:center"> Fig. 内存条、内存空间、内存地址 </p>
<p align="center"> Fig. 内存条、内存空间、内存地址 </p>
**内存资源是设计数据结构与算法的重要考虑因素。** 内存是所有程序的公共资源,当内存被某程序占用时,不能被其它程序同时使用。我们需要根据剩余内存资源的情况来设计算法。例如,若剩余内存空间有限,则要求算法占用的峰值内存不能超过系统剩余内存;若运行的程序很多、缺少大块连续的内存空间,则要求选取的数据结构必须能够存储在离散的内存空间内。

View file

@ -77,7 +77,7 @@ comments: true
![relationship_between_data_structure_and_algorithm](index.assets/relationship_between_data_structure_and_algorithm.png)
<p style="text-align:center"> Fig. 数据结构与算法的关系 </p>
<p align="center"> Fig. 数据结构与算法的关系 </p>
!!! tip "约定俗成的习惯"

View file

@ -22,8 +22,8 @@ comments: true
!!! quote ""
<p style="text-align:center"> 追风赶月莫停留,平芜尽处是春山 </p>
<p style="text-align:center"> 一起加油! </p>
<p align="center"> 追风赶月莫停留,平芜尽处是春山 </p>
<p align="center"> 一起加油! </p>
## 内容结构
@ -31,7 +31,7 @@ comments: true
![mindmap](index.assets/mindmap.png)
<p style="text-align:center"> Fig. 知识点思维导图 </p>
<p align="center"> Fig. 知识点思维导图 </p>
### 复杂度分析

View file

@ -4,7 +4,7 @@
- 二分查找利用数据的有序性,通过循环不断缩小一半搜索区间来实现查找,其要求输入数据是有序的,并且仅适用于数组或基于数组实现的数据结构。
- 哈希查找借助哈希表来实现常数阶时间复杂度的查找操作,体现以空间换时间的算法思想。
<p style="text-align:center"> Table. 三种查找方法对比 </p>
<p align="center"> Table. 三种查找方法对比 </p>
<div class="center-table" markdown>

View file

@ -42,6 +42,8 @@ comments: true
![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png)
<p align="center"> Fig. 冒泡操作 </p>
## 算法流程
1. 设数组长度为 $n$ ,完成第一轮「冒泡」后,数组最大元素已在正确位置,接下来只需排序剩余 $n - 1$ 个元素。
@ -52,6 +54,8 @@ comments: true
![bubble_sort](bubble_sort.assets/bubble_sort.png)
<p align="center"> Fig. 冒泡排序流程 </p>
=== "Java"
```java

View file

@ -12,6 +12,8 @@ comments: true
![insertion_operation](insertion_sort.assets/insertion_operation.png)
<p align="center"> Fig. 插入操作 </p>
## 算法流程
1. 第 1 轮先选取数组的 **第 2 个元素**`base` ,执行「插入操作」后, **数组前 2 个元素已完成排序**
@ -22,6 +24,8 @@ comments: true
![insertion_sort](insertion_sort.assets/insertion_sort.png)
<p align="center"> Fig. 插入排序流程 </p>
=== "Java"
```java

View file

@ -33,7 +33,7 @@ comments: true
=== "Step 9"
![pivot_division_step9](quick_sort.assets/pivot_division_step9.png)
哨兵划分实现代码如下。
<p align="center"> Fig. 哨兵划分 </p>
=== "Java"
@ -75,6 +75,8 @@ comments: true
![quick_sort](quick_sort.assets/quick_sort.png)
<p align="center"> Fig. 快速排序流程 </p>
=== "Java"
```java

View file

@ -4,13 +4,13 @@
![deque_operations](deque.assets/deque_operations.png)
<p style="text-align:center"> Fig. 双向队列的操作 </p>
<p align="center"> Fig. 双向队列的操作 </p>
## 双向队列常用操作
双向队列的常用操作见下表,方法名需根据编程语言设定来具体确定。
<p style="text-align:center"> Table. 双向队列的常用操作 </p>
<p align="center"> Table. 双向队列的常用操作 </p>
<div class="center-table" markdown>

View file

@ -10,13 +10,13 @@ comments: true
![queue_operations](queue.assets/queue_operations.png)
<p style="text-align:center"> Fig. 队列的先入先出特性 </p>
<p align="center"> Fig. 队列的先入先出特性 </p>
## 队列常用操作
队列的常用操作见下表,方法命名需根据编程语言的设定来具体确定。
<p style="text-align:center"> Table. 队列的常用操作 </p>
<p align="center"> Table. 队列的常用操作 </p>
<div class="center-table" markdown>

View file

@ -10,13 +10,13 @@ comments: true
![stack_operations](stack.assets/stack_operations.png)
<p style="text-align:center"> Fig. 栈的先入后出特性 </p>
<p align="center"> Fig. 栈的先入后出特性 </p>
## 栈常用操作
栈的常用操作见下表,方法名需根据编程语言设定来具体确定。
<p style="text-align:center"> Table. 栈的常用操作 </p>
<p align="center"> Table. 栈的常用操作 </p>
<div class="center-table" markdown>

View file

@ -22,7 +22,7 @@ comments: true
![binary_tree_definition](binary_tree.assets/binary_tree_definition.png)
<p style="text-align:center"> Fig. 子结点与子树 </p>
<p align="center"> Fig. 子结点与子树 </p>
需要注意,父结点、子结点、子树是可以向下递推的。例如,如果将上图的「结点 2」看作父结点那么其左子节点和右子结点分别为「结点 4」和「结点 5」左子树和右子树分别为「结点 4 以下的树」和「结点 5 以下的树」。
@ -42,7 +42,7 @@ comments: true
![binary_tree_terminology](binary_tree.assets/binary_tree_terminology.png)
<p style="text-align:center"> Fig. 二叉树的常见术语 </p>
<p align="center"> Fig. 二叉树的常见术语 </p>
## 二叉树最佳和最差结构
@ -50,7 +50,7 @@ comments: true
![binary_tree_corner_cases](binary_tree.assets/binary_tree_corner_cases.png)
<p style="text-align:center"> Fig. 二叉树的最佳和最差结构 </p>
<p align="center"> Fig. 二叉树的最佳和最差结构 </p>
在最佳和最差结构下,二叉树的结点数量和高度等性质达到最大(最小)值。
@ -88,7 +88,7 @@ comments: true
![binary_tree_add_remove](binary_tree.assets/binary_tree_add_remove.png)
<p style="text-align:center"> Fig. 在二叉树中插入与删除结点 </p>
<p align="center"> Fig. 在二叉树中插入与删除结点 </p>
```java title="binary_tree.java"
TreeNode P = new TreeNode(0);
@ -115,7 +115,7 @@ n1.left = n2;
![binary_tree_bfs](binary_tree.assets/binary_tree_bfs.png)
<p style="text-align:center"> Fig. 二叉树的层序遍历 </p>
<p align="center"> Fig. 二叉树的层序遍历 </p>
广度优先遍历一般借助「队列」来实现。队列的规则是 “先进先出” ,广度优先遍历的规则是 ”一层层平推“ ,两者背后的思想是一致的。
@ -148,7 +148,7 @@ n1.left = n2;
![binary_tree_dfs](binary_tree.assets/binary_tree_dfs.png)
<p style="text-align:center"> Fig. 二叉树的前 / 中 / 后序遍历 </p>
<p align="center"> Fig. 二叉树的前 / 中 / 后序遍历 </p>
<div class="center-table" markdown>

View file

@ -21,7 +21,7 @@ hide:
<h2 style="text-align:center"> 「清晰动画讲解」 </h2>
<p style="text-align:center"> 借助动画介绍重点,提升知识吸收效率</br>HTML 文档,支持笔记本、平板、手机多种终端 </p>
<p align="center"> 借助动画介绍重点,提升知识吸收效率</br>HTML 文档,支持笔记本、平板、手机多种终端 </p>
![algorithm_animation](index.assets/animation.gif)
@ -29,7 +29,7 @@ hide:
<h2 style="text-align:center"> 「代码实践导向」 </h2>
<p style="text-align:center"> 示例代码皆可一键运行,在调试中加深理解</br>提供 Java, C++, Python 源码与详细注释 </p>
<p align="center"> 示例代码皆可一键运行,在调试中加深理解</br>提供 Java, C++, Python 源码与详细注释 </p>
![running_code](index.assets/running_code.gif)
@ -37,7 +37,7 @@ hide:
<h2 style="text-align:center"> 「可讨论与提问」 </h2>
<p style="text-align:center"> 在评论区与小伙伴们一起学习进步</br>作者定期回复评论问题(一般 < 72h </p>
<p align="center"> 在评论区与小伙伴们一起学习进步</br>作者定期回复评论问题(一般 < 72h </p>
![comment](index.assets/comment.gif)