mirror of
https://github.com/krahets/hello-algo.git
synced 2025-02-02 22:43:50 +08:00
feat: modify some Dart codes and add Dart code blocks to the docs (#543)
This commit is contained in:
parent
53e18bc6d6
commit
281c0c618a
@ -6,9 +6,9 @@
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
/* 随机返回一个 数组元素 */
|
||||
/* 随机返回一个数组元素 */
|
||||
int randomAccess(List nums) {
|
||||
// 在区间[0,size) 中随机抽取一个数字
|
||||
// 在区间 [0, nums.length) 中随机抽取一个数字
|
||||
int randomIndex = Random().nextInt(nums.length);
|
||||
// 获取并返回随机元素
|
||||
int randomNum = nums[randomIndex];
|
||||
@ -17,9 +17,8 @@ int randomAccess(List nums) {
|
||||
|
||||
/* 扩展数组长度 */
|
||||
List extend(List nums, int enlarge) {
|
||||
// 初始化一个扩展长度后的数组,元素初始值为0
|
||||
// 初始化一个扩展长度后的数组
|
||||
List<int> res = List.filled(nums.length + enlarge, 0);
|
||||
|
||||
// 将原数组中的所有元素复制到新数组
|
||||
for (var i = 0; i < nums.length; i++) {
|
||||
res[i] = nums[i];
|
||||
@ -30,7 +29,7 @@ List extend(List nums, int enlarge) {
|
||||
|
||||
/* 在数组的索引 index 处插入元素 num */
|
||||
void insert(List nums, int num, int index) {
|
||||
// 把索引index以及之后的所有元素向后移动一位
|
||||
// 把索引 index 以及之后的所有元素向后移动一位
|
||||
for (var i = nums.length - 1; i > index; i--) {
|
||||
nums[i] = nums[i - 1];
|
||||
}
|
||||
@ -40,6 +39,7 @@ void insert(List nums, int num, int index) {
|
||||
|
||||
/* 删除索引 index 处元素 */
|
||||
void remove(List nums, int index) {
|
||||
// 把索引 index 之后的所有元素向前移动一位
|
||||
for (var i = index; i < nums.length - 1; i++) {
|
||||
nums[i] = nums[i + 1];
|
||||
}
|
||||
@ -56,7 +56,7 @@ void traverse(List nums) {
|
||||
for (var num in nums) {
|
||||
count++;
|
||||
}
|
||||
// 通过forEach方法遍历数组
|
||||
// 通过 forEach 方法遍历数组
|
||||
nums.forEach((element) {
|
||||
count++;
|
||||
});
|
||||
@ -71,8 +71,8 @@ int find(List nums, int target) {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化固定长度数组 */
|
||||
void main() {
|
||||
/* 初始化数组 */
|
||||
var arr = List.filled(5, 0);
|
||||
print('数组 arr = $arr');
|
||||
List nums = [1, 3, 2, 5, 4];
|
||||
@ -100,6 +100,4 @@ int main() {
|
||||
/* 查找元素 */
|
||||
int index = find(nums, 3);
|
||||
print("在 nums 中查找元素 3 ,得到索引 = $index");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ void insert(ListNode n0, ListNode P) {
|
||||
/* 删除链表的节点 n0 之后的首个节点 */
|
||||
void remove(ListNode n0) {
|
||||
if (n0.next == null) return;
|
||||
// n0 -> P -> n1
|
||||
ListNode P = n0.next!;
|
||||
ListNode? n1 = P.next;
|
||||
n0.next = n1;
|
||||
@ -45,9 +46,9 @@ int find(ListNode? head, int target) {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
// 初始化链表
|
||||
//初始化各个节点
|
||||
// 初始化各个节点
|
||||
ListNode n0 = ListNode(1);
|
||||
ListNode n1 = ListNode(3);
|
||||
ListNode n2 = ListNode(2);
|
||||
@ -79,6 +80,4 @@ int main() {
|
||||
/* 查找节点 */
|
||||
int index = find(n0, 2);
|
||||
print('链表中值为 2 的节点的索引 = $index');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
/* 初始化列表 */
|
||||
List<int> list = [1, 3, 2, 5, 4];
|
||||
print('列表 list = $list');
|
||||
@ -57,6 +57,4 @@ int main() {
|
||||
/* 排序列表 */
|
||||
list.sort();
|
||||
print('排序列表后 list = $list');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class MyList {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
/* 初始化列表 */
|
||||
MyList list = MyList();
|
||||
/* 尾部添加元素 */
|
||||
@ -129,6 +129,4 @@ int main() {
|
||||
}
|
||||
print(
|
||||
'扩容后的列表 list = ${list.toArray()} ,容量 = ${list.capacity()} ,长度 = ${list.size()}');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ TreeNode? buildTree(int n) {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
int n = 5;
|
||||
// 常数阶
|
||||
constant(n);
|
||||
@ -102,5 +102,4 @@ int main() {
|
||||
// 指数阶
|
||||
TreeNode? root = buildTree(n);
|
||||
printTree(root);
|
||||
return 0;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ int factorialRecur(int n) {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势
|
||||
int n = 8;
|
||||
print('输入数据大小 n = $n');
|
||||
@ -160,5 +160,4 @@ int main() {
|
||||
|
||||
count = factorialRecur(n);
|
||||
print('阶乘阶(递归实现)的计算操作数量 = $count');
|
||||
return 0;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ int findOne(List<int> nums) {
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
void main() {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
int n = 100;
|
||||
final nums = randomNumbers(n);
|
||||
@ -37,6 +37,4 @@ int main() {
|
||||
print('\n数组 [ 1, 2, ..., n ] 被打乱后 = $nums');
|
||||
print('数字 1 的索引为 + $index');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ void main() {
|
||||
print("\n遍历键值对 Key->Value");
|
||||
map.forEach((key, value) => print("$key -> $value"));
|
||||
print("\n单独遍历键 Key");
|
||||
map.forEach((key, value) => print("$key"));
|
||||
map.keys.forEach((key) => print(key));
|
||||
print("\n单独遍历值 Value");
|
||||
map.forEach((key, value) => print("$value"));
|
||||
map.values.forEach((value) => print(value));
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ void main() {
|
||||
print("元素 1 队首入队后 deque = $deque");
|
||||
|
||||
/* 元素出队 */
|
||||
|
||||
final int popLast = deque.removeLast();
|
||||
print("队尾出队元素 = $popLast,队尾出队后 deque = $deque");
|
||||
final int popFirst = deque.removeFirst();
|
||||
|
@ -95,7 +95,9 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="array.dart"
|
||||
|
||||
/* 初始化数组 */
|
||||
List<int> arr = List.filled(5, 0); // [0, 0, 0, 0, 0]
|
||||
List<int> nums = [1, 3, 2, 5, 4];
|
||||
```
|
||||
|
||||
## 数组优点
|
||||
|
@ -156,7 +156,12 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 链表节点类 */
|
||||
class ListNode {
|
||||
int val; // 节点值
|
||||
ListNode? next; // 指向下一节点的指针(引用)
|
||||
ListNode(this.val, [this.next]); // 构造函数
|
||||
}
|
||||
```
|
||||
|
||||
!!! question "尾节点指向什么?"
|
||||
@ -342,7 +347,18 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="linked_list.dart"
|
||||
|
||||
/* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */\
|
||||
// 初始化各个节点
|
||||
ListNode n0 = ListNode(1);
|
||||
ListNode n1 = ListNode(3);
|
||||
ListNode n2 = ListNode(2);
|
||||
ListNode n3 = ListNode(5);
|
||||
ListNode n4 = ListNode(4);
|
||||
// 构建引用指向
|
||||
n0.next = n1;
|
||||
n1.next = n2;
|
||||
n2.next = n3;
|
||||
n3.next = n4;
|
||||
```
|
||||
|
||||
## 链表优点
|
||||
@ -799,7 +815,13 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 双向链表节点类 */
|
||||
class ListNode {
|
||||
int val; // 节点值
|
||||
ListNode next; // 指向后继节点的指针(引用)
|
||||
ListNode prev; // 指向前驱节点的指针(引用)
|
||||
ListNode(this.val, [this.next, this.prev]); // 构造函数
|
||||
}
|
||||
```
|
||||
|
||||
![常见链表种类](linked_list.assets/linkedlist_common_types.png)
|
||||
|
@ -109,7 +109,11 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
|
||||
/* 初始化列表 */
|
||||
// 无初始值
|
||||
List<int> list1 = [];
|
||||
// 有初始值
|
||||
List<int> list = [1, 3, 2, 5, 4];
|
||||
```
|
||||
|
||||
**访问与更新元素**。由于列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问和更新元素,效率很高。
|
||||
@ -213,7 +217,11 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
/* 访问元素 */
|
||||
int num = list[1]; // 访问索引 1 处的元素
|
||||
|
||||
/* 更新元素 */
|
||||
list[1] = 0; // 将索引 1 处的元素更新为 0
|
||||
```
|
||||
|
||||
**在列表中添加、插入、删除元素**。相较于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但插入和删除元素的效率仍与数组相同,时间复杂度为 $O(N)$ 。
|
||||
@ -407,7 +415,21 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
/* 清空列表 */
|
||||
list.clear();
|
||||
|
||||
/* 尾部添加元素 */
|
||||
list.add(1);
|
||||
list.add(3);
|
||||
list.add(2);
|
||||
list.add(5);
|
||||
list.add(4);
|
||||
|
||||
/* 中间插入元素 */
|
||||
list.insert(3, 6); // 在索引 3 处插入数字 6
|
||||
|
||||
/* 删除元素 */
|
||||
list.removeAt(3); // 删除索引 3 处的元素
|
||||
```
|
||||
|
||||
**遍历列表**。与数组一样,列表可以根据索引遍历,也可以直接遍历各元素。
|
||||
@ -566,7 +588,17 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
/* 通过索引遍历列表 */
|
||||
int count = 0;
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
count++;
|
||||
}
|
||||
|
||||
/* 直接遍历列表元素 */
|
||||
count = 0;
|
||||
for (int n in list) {
|
||||
count++;
|
||||
}
|
||||
```
|
||||
|
||||
**拼接两个列表**。给定一个新列表 `list1` ,我们可以将该列表拼接到原列表的尾部。
|
||||
@ -655,7 +687,9 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
|
||||
/* 拼接两个列表 */
|
||||
List<int> list1 = [6, 8, 7, 10, 9];
|
||||
list.addAll(list1); // 将列表 list1 拼接到 list 之后
|
||||
```
|
||||
|
||||
**排序列表**。排序也是常用的方法之一。完成列表排序后,我们便可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法。
|
||||
@ -732,7 +766,8 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="list.dart"
|
||||
|
||||
/* 排序列表 */
|
||||
list.sort(); // 排序后,列表元素从小到大排列
|
||||
```
|
||||
|
||||
## 列表实现 *
|
||||
|
@ -526,7 +526,26 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 回溯算法框架 */
|
||||
void backtrack(State state, List<Choice>, List<State> res) {
|
||||
// 判断是否为解
|
||||
if (isSolution(state)) {
|
||||
// 记录解
|
||||
recordSolution(state, res);
|
||||
return;
|
||||
}
|
||||
// 遍历所有选择
|
||||
for (Choice choice in choices) {
|
||||
// 剪枝:判断选择是否合法
|
||||
if (isValid(state, choice)) {
|
||||
// 尝试:做出选择,更新状态
|
||||
makeChoice(state, choice);
|
||||
backtrack(state, choices, res);
|
||||
// 回退:撤销选择,恢复到之前的状态
|
||||
undoChoice(state, choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
下面,我们尝试基于此框架来解决例题三。在例题三中,状态 `state` 是节点遍历路径,选择 `choices` 是当前节点的左子节点和右子节点,结果 `res` 是路径列表,实现代码如下所示。
|
||||
|
@ -261,7 +261,26 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
/* 类 */
|
||||
class Node {
|
||||
int val;
|
||||
Node next;
|
||||
Node(this.val, [this.next]);
|
||||
}
|
||||
|
||||
/* 函数 */
|
||||
int function() {
|
||||
// do something...
|
||||
return 0;
|
||||
}
|
||||
|
||||
int algorithm(int n) { // 输入数据
|
||||
const int a = 0; // 暂存数据(常量)
|
||||
int b = 0; // 暂存数据(变量)
|
||||
Node node = Node(0); // 暂存数据(对象)
|
||||
int c = function(); // 栈帧空间(调用函数)
|
||||
return a + b + c; // 输出数据
|
||||
}
|
||||
```
|
||||
|
||||
## 推算方法
|
||||
@ -389,7 +408,13 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
void algorithm(int n) {
|
||||
int a = 0; // O(1)
|
||||
List<int> b = List.filled(10000, 0); // O(1)
|
||||
if (n > 10) {
|
||||
List<int> nums = List.filled(n, 0); // O(n)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**在递归函数中,需要注意统计栈帧空间**。例如,函数 `loop()` 在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。而递归函数 `recur()` 在运行过程中会同时存在 $n$ 个未返回的 `recur()` ,从而占用 $O(n)$ 的栈帧空间。
|
||||
@ -594,7 +619,21 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
int function() {
|
||||
// do something
|
||||
return 0;
|
||||
}
|
||||
/* 循环 O(1) */
|
||||
void loop(int n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
function();
|
||||
}
|
||||
}
|
||||
/* 递归 O(n) */
|
||||
void recur(int n) {
|
||||
if (n == 1) return;
|
||||
return recur(n - 1);
|
||||
}
|
||||
```
|
||||
|
||||
## 常见类型
|
||||
|
@ -158,7 +158,16 @@ $$
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
// 在某运行平台下
|
||||
void algorithm(int n) {
|
||||
int a = 2; // 1 ns
|
||||
a = a + 1; // 1 ns
|
||||
a = a * 2; // 10 ns
|
||||
// 循环 n 次
|
||||
for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++
|
||||
print(0); // 5 ns
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然而实际上,**统计算法的运行时间既不合理也不现实**。首先,我们不希望预估时间和运行平台绑定,因为算法需要在各种不同的平台上运行。其次,我们很难获知每种操作的运行时间,这给预估过程带来了极大的难度。
|
||||
@ -374,7 +383,22 @@ $$
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
// 算法 A 时间复杂度:常数阶
|
||||
void algorithmA(int n) {
|
||||
print(0);
|
||||
}
|
||||
// 算法 B 时间复杂度:线性阶
|
||||
void algorithmB(int n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
print(0);
|
||||
}
|
||||
}
|
||||
// 算法 C 时间复杂度:常数阶
|
||||
void algorithmC(int n) {
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
print(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
![算法 A, B, C 的时间增长趋势](time_complexity.assets/time_complexity_simple_example.png)
|
||||
@ -530,7 +554,15 @@ $$
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
void algorithm(int n) {
|
||||
int a = 1; // +1
|
||||
a = a + 1; // +1
|
||||
a = a * 2; // +1
|
||||
// 循环 n 次
|
||||
for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++)
|
||||
print(0); // +1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
$T(n)$ 是一次函数,说明时间增长趋势是线性的,因此可以得出时间复杂度是线性阶。
|
||||
@ -760,7 +792,20 @@ $$
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
void algorithm(int n) {
|
||||
int a = 1; // +0(技巧 1)
|
||||
a = a + n; // +0(技巧 1)
|
||||
// +n(技巧 2)
|
||||
for (int i = 0; i < 5 * n + 1; i++) {
|
||||
print(0);
|
||||
}
|
||||
// +n*n(技巧 3)
|
||||
for (int i = 0; i < 2 * n; i++) {
|
||||
for (int j = 0; j < n + 1; j++) {
|
||||
print(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2) 判断渐近上界
|
||||
|
@ -133,5 +133,9 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 使用多种「基本数据类型」来初始化「数组」 */
|
||||
List<int> numbers = List.filled(5, 0);
|
||||
List<double> decimals = List.filled(5, 0.0);
|
||||
List<String> characters = List.filled(5, 'a');
|
||||
List<bool> booleans = List.filled(5, false);
|
||||
```
|
||||
|
@ -226,7 +226,24 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map.dart"
|
||||
/* 初始化哈希表 */
|
||||
Map<int, String> map = {};
|
||||
|
||||
/* 添加操作 */
|
||||
// 在哈希表中添加键值对 (key, value)
|
||||
map[12836] = "小哈";
|
||||
map[15937] = "小啰";
|
||||
map[16750] = "小算";
|
||||
map[13276] = "小法";
|
||||
map[10583] = "小鸭";
|
||||
|
||||
/* 查询操作 */
|
||||
// 向哈希表输入键 key ,得到值 value
|
||||
String name = map[15937];
|
||||
|
||||
/* 删除操作 */
|
||||
// 在哈希表中删除键值对 (key, value)
|
||||
map.remove(10583);
|
||||
```
|
||||
|
||||
遍历哈希表有三种方式,即 **遍历键值对、遍历键、遍历值**。
|
||||
@ -390,7 +407,21 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map.dart"
|
||||
/* 遍历哈希表 */
|
||||
// 遍历键值对 Key->Value
|
||||
map.forEach((key, value) {
|
||||
print('$key -> $value');
|
||||
});
|
||||
|
||||
// 单独遍历键 Key
|
||||
map.keys.forEach((key) {
|
||||
print(key);
|
||||
});
|
||||
|
||||
// 单独遍历值 Value
|
||||
map.values.forEach((value) {
|
||||
print(value);
|
||||
});
|
||||
```
|
||||
|
||||
## 哈希函数
|
||||
|
@ -304,7 +304,7 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="heap.dart"
|
||||
|
||||
// Dart 未提供内置 Heap 类
|
||||
```
|
||||
|
||||
## 堆的实现
|
||||
|
@ -147,12 +147,14 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
// 标题注释,用于标注函数、类、测试样例等
|
||||
/* 标题注释,用于标注函数、类、测试样例等 */
|
||||
|
||||
// 内容注释,用于详解代码
|
||||
|
||||
// 多行
|
||||
// 注释
|
||||
/**
|
||||
* 多行
|
||||
* 注释
|
||||
*/
|
||||
```
|
||||
|
||||
## 在动画图解中高效学习
|
||||
|
@ -286,7 +286,30 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="deque.dart"
|
||||
/* 初始化双向队列 */
|
||||
// 在 Dart 中,Queue 被定义为双向队列
|
||||
Queue<int> deque = Queue<int>();
|
||||
|
||||
/* 元素入队 */
|
||||
deque.addLast(2); // 添加至队尾
|
||||
deque.addLast(5);
|
||||
deque.addLast(4);
|
||||
deque.addFirst(3); // 添加至队首
|
||||
deque.addFirst(1);
|
||||
|
||||
/* 访问元素 */
|
||||
int peekFirst = deque.first; // 队首元素
|
||||
int peekLast = deque.last; // 队尾元素
|
||||
|
||||
/* 元素出队 */
|
||||
int popFirst = deque.removeFirst(); // 队首元素出队
|
||||
int popLast = deque.removeLast(); // 队尾元素出队
|
||||
|
||||
/* 获取双向队列的长度 */
|
||||
int size = deque.length;
|
||||
|
||||
/* 判断双向队列是否为空 */
|
||||
bool isEmpty = deque.isEmpty;W
|
||||
```
|
||||
|
||||
## 双向队列实现 *
|
||||
|
@ -255,7 +255,28 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="queue.dart"
|
||||
/* 初始化队列 */
|
||||
// 在 Dart 中,队列类 Qeque 是双向队列,也可作为队列使用
|
||||
Queue<int> queue = Queue();
|
||||
|
||||
/* 元素入队 */
|
||||
queue.add(1);
|
||||
queue.add(3);
|
||||
queue.add(2);
|
||||
queue.add(5);
|
||||
queue.add(4);
|
||||
|
||||
/* 访问队首元素 */
|
||||
int peek = queue.first;
|
||||
|
||||
/* 元素出队 */
|
||||
int pop = queue.removeFirst();
|
||||
|
||||
/* 获取队列的长度 */
|
||||
int size = queue.length;
|
||||
|
||||
/* 判断队列是否为空 */
|
||||
bool isEmpty = queue.isEmpty;
|
||||
```
|
||||
|
||||
## 队列实现
|
||||
|
@ -253,7 +253,28 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="stack.dart"
|
||||
/* 初始化栈 */
|
||||
// Dart 没有内置的栈类,可以把 List 当作栈来使用
|
||||
List<int> stack = [];
|
||||
|
||||
/* 元素入栈 */
|
||||
stack.add(1);
|
||||
stack.add(3);
|
||||
stack.add(2);
|
||||
stack.add(5);
|
||||
stack.add(4);
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
int peek = stack.last;
|
||||
|
||||
/* 元素出栈 */
|
||||
int pop = stack.removeLast();
|
||||
|
||||
/* 获取栈的长度 */
|
||||
int size = stack.length;
|
||||
|
||||
/* 判断是否为空 */
|
||||
bool isEmpty = stack.isEmpty;
|
||||
```
|
||||
|
||||
## 栈的实现
|
||||
|
@ -103,7 +103,9 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 二叉树的数组表示 */
|
||||
// 使用 int? 可空类型 ,就可以使用 null 来标记空位
|
||||
List<int?> tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15];
|
||||
```
|
||||
|
||||
![任意类型二叉树的数组表示](binary_tree.assets/array_representation_with_empty.png)
|
||||
|
@ -172,7 +172,14 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* AVL 树节点类 */
|
||||
class TreeNode {
|
||||
int val; // 节点值
|
||||
int height; // 节点高度
|
||||
TreeNode? left; // 左子节点
|
||||
TreeNode? right; // 右子节点
|
||||
TreeNode(this.val, [this.height = 0, this.left, this.right]);
|
||||
}
|
||||
```
|
||||
|
||||
「节点高度」是指从该节点到最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 0 ,而空节点的高度为 -1 。我们将创建两个工具函数,分别用于获取和更新节点的高度。
|
||||
|
@ -146,7 +146,13 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title=""
|
||||
|
||||
/* 二叉树节点类 */
|
||||
class TreeNode {
|
||||
int val; // 节点值
|
||||
TreeNode? left; // 左子节点指针
|
||||
TreeNode? right; // 右子节点指针
|
||||
TreeNode(this.val, [this.left, this.right]);
|
||||
}
|
||||
```
|
||||
|
||||
节点的两个指针分别指向「左子节点」和「右子节点」,同时该节点被称为这两个子节点的「父节点」。当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成的树称为该节点的「左子树」,同理可得「右子树」。
|
||||
@ -338,7 +344,18 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree.dart"
|
||||
|
||||
/* 初始化二叉树 */
|
||||
// 初始化节点
|
||||
TreeNode n1 = new TreeNode(1);
|
||||
TreeNode n2 = new TreeNode(2);
|
||||
TreeNode n3 = new TreeNode(3);
|
||||
TreeNode n4 = new TreeNode(4);
|
||||
TreeNode n5 = new TreeNode(5);
|
||||
// 构建引用指向(即指针)
|
||||
n1.left = n2;
|
||||
n1.right = n3;
|
||||
n2.left = n4;
|
||||
n2.right = n5;
|
||||
```
|
||||
|
||||
**插入与删除节点**。与链表类似,通过修改指针来实现插入与删除节点。
|
||||
@ -460,7 +477,13 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="binary_tree.dart"
|
||||
|
||||
/* 插入与删除节点 */
|
||||
TreeNode P = new TreeNode(0);
|
||||
// 在 n1 -> n2 中间插入节点 P
|
||||
n1.left = P;
|
||||
P.left = n2;
|
||||
// 删除节点 P
|
||||
n1.left = n2;
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
Loading…
Reference in New Issue
Block a user