mirror of
https://github.com/krahets/hello-algo.git
synced 2025-07-06 05:48:58 +08:00
build
This commit is contained in:
@ -512,7 +512,7 @@ By comparison, inserting an element into an array has a time complexity of $O(n)
|
||||
/* 在链表的节点 n0 之后插入节点 P */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn insert<T>(n0: &Rc<RefCell<ListNode<T>>>, P: Rc<RefCell<ListNode<T>>>) {
|
||||
let n1 = n0.borrow_mut().next.take();
|
||||
let n1 = n0.borrow_mut().next.take();
|
||||
P.borrow_mut().next = n1;
|
||||
n0.borrow_mut().next = Some(P);
|
||||
}
|
||||
@ -690,7 +690,9 @@ It's important to note that even though node `P` continues to point to `n1` afte
|
||||
/* 删除链表的节点 n0 之后的首个节点 */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn remove<T>(n0: &Rc<RefCell<ListNode<T>>>) {
|
||||
if n0.borrow().next.is_none() {return};
|
||||
if n0.borrow().next.is_none() {
|
||||
return;
|
||||
};
|
||||
// n0 -> P -> n1
|
||||
let P = n0.borrow_mut().next.take();
|
||||
if let Some(node) = P {
|
||||
@ -872,10 +874,13 @@ It's important to note that even though node `P` continues to point to `n1` afte
|
||||
```rust title="linked_list.rs"
|
||||
/* 访问链表中索引为 index 的节点 */
|
||||
pub fn access<T>(head: Rc<RefCell<ListNode<T>>>, index: i32) -> Rc<RefCell<ListNode<T>>> {
|
||||
if index <= 0 {return head};
|
||||
if let Some(node) = &head.borrow_mut().next {
|
||||
if index <= 0 {
|
||||
return head;
|
||||
};
|
||||
if let Some(node) = &head.borrow().next {
|
||||
return access(node.clone(), index - 1);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
```
|
||||
@ -1071,7 +1076,9 @@ Traverse the linked list to locate a node whose value matches `target`, and then
|
||||
```rust title="linked_list.rs"
|
||||
/* 在链表中查找值为 target 的首个节点 */
|
||||
pub fn find<T: PartialEq>(head: Rc<RefCell<ListNode<T>>>, target: T, index: i32) -> i32 {
|
||||
if head.borrow().val == target {return index};
|
||||
if head.borrow().val == target {
|
||||
return index;
|
||||
};
|
||||
if let Some(node) = &head.borrow_mut().next {
|
||||
return find(node.clone(), target, index + 1);
|
||||
}
|
||||
|
@ -1785,16 +1785,16 @@ To enhance our understanding of how lists work, we will attempt to implement a s
|
||||
#[allow(dead_code)]
|
||||
struct MyList {
|
||||
arr: Vec<i32>, // 数组(存储列表元素)
|
||||
capacity: usize, // 列表容量
|
||||
size: usize, // 列表长度(当前元素数量)
|
||||
extend_ratio: usize, // 每次列表扩容的倍数
|
||||
capacity: usize, // 列表容量
|
||||
size: usize, // 列表长度(当前元素数量)
|
||||
extend_ratio: usize, // 每次列表扩容的倍数
|
||||
}
|
||||
|
||||
#[allow(unused,unused_comparisons)]
|
||||
#[allow(unused, unused_comparisons)]
|
||||
impl MyList {
|
||||
/* 构造方法 */
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
let mut vec = Vec::new();
|
||||
let mut vec = Vec::new();
|
||||
vec.resize(capacity, 0);
|
||||
Self {
|
||||
arr: vec,
|
||||
@ -1817,13 +1817,17 @@ To enhance our understanding of how lists work, we will attempt to implement a s
|
||||
/* 访问元素 */
|
||||
pub fn get(&self, index: usize) -> i32 {
|
||||
// 索引如果越界,则抛出异常,下同
|
||||
if index >= self.size {panic!("索引越界")};
|
||||
if index >= self.size {
|
||||
panic!("索引越界")
|
||||
};
|
||||
return self.arr[index];
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
pub fn set(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size {panic!("索引越界")};
|
||||
if index >= self.size {
|
||||
panic!("索引越界")
|
||||
};
|
||||
self.arr[index] = num;
|
||||
}
|
||||
|
||||
@ -1840,7 +1844,9 @@ To enhance our understanding of how lists work, we will attempt to implement a s
|
||||
|
||||
/* 在中间插入元素 */
|
||||
pub fn insert(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size() {panic!("索引越界")};
|
||||
if index >= self.size() {
|
||||
panic!("索引越界")
|
||||
};
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if self.size == self.capacity() {
|
||||
self.extend_capacity();
|
||||
@ -1856,7 +1862,9 @@ To enhance our understanding of how lists work, we will attempt to implement a s
|
||||
|
||||
/* 删除元素 */
|
||||
pub fn remove(&mut self, index: usize) -> i32 {
|
||||
if index >= self.size() {panic!("索引越界")};
|
||||
if index >= self.size() {
|
||||
panic!("索引越界")
|
||||
};
|
||||
let num = self.arr[index];
|
||||
// 将将索引 index 之后的元素都向前移动一位
|
||||
for j in (index..self.size - 1) {
|
||||
|
@ -151,7 +151,7 @@ The following function implements the sum $1 + 2 + \dots + n$ using a `for` loop
|
||||
res += i;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -352,6 +352,7 @@ Below we use a `while` loop to implement the sum $1 + 2 + \dots + n$:
|
||||
fn while_loop(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // 初始化条件变量
|
||||
|
||||
// 循环求和 1, 2, ..., n-1, n
|
||||
while i <= n {
|
||||
res += i;
|
||||
@ -570,6 +571,7 @@ For example, in the following code, the condition variable $i$ is updated twice
|
||||
fn while_loop_ii(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // 初始化条件变量
|
||||
|
||||
// 循环求和 1, 4, 10, ...
|
||||
while i <= n {
|
||||
res += i;
|
||||
|
@ -979,7 +979,7 @@ Note that memory occupied by initializing variables or calling functions in a lo
|
||||
|
||||
```rust title="space_complexity.rs"
|
||||
/* 函数 */
|
||||
fn function() ->i32 {
|
||||
fn function() -> i32 {
|
||||
// 执行某些操作
|
||||
return 0;
|
||||
}
|
||||
@ -1452,7 +1452,9 @@ As shown below, this function's recursive depth is $n$, meaning there are $n$ in
|
||||
/* 线性阶(递归实现) */
|
||||
fn linear_recur(n: i32) {
|
||||
println!("递归 n = {}", n);
|
||||
if n == 1 {return};
|
||||
if n == 1 {
|
||||
return;
|
||||
};
|
||||
linear_recur(n - 1);
|
||||
}
|
||||
```
|
||||
@ -1834,7 +1836,9 @@ As shown below, the recursive depth of this function is $n$, and in each recursi
|
||||
```rust title="space_complexity.rs"
|
||||
/* 平方阶(递归实现) */
|
||||
fn quadratic_recur(n: i32) -> i32 {
|
||||
if n <= 0 {return 0};
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
};
|
||||
// 数组 nums 长度为 n, n-1, ..., 2, 1
|
||||
let nums = vec![0; n as usize];
|
||||
println!("递归 n = {} 中的 nums 长度 = {}", n, nums.len());
|
||||
@ -2011,7 +2015,9 @@ Exponential order is common in binary trees. Observe the below image, a "full bi
|
||||
```rust title="space_complexity.rs"
|
||||
/* 指数阶(建立满二叉树) */
|
||||
fn build_tree(n: i32) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
if n == 0 {return None};
|
||||
if n == 0 {
|
||||
return None;
|
||||
};
|
||||
let root = TreeNode::new(0);
|
||||
root.borrow_mut().left = build_tree(n - 1);
|
||||
root.borrow_mut().right = build_tree(n - 1);
|
||||
|
@ -1737,7 +1737,7 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l
|
||||
int count = 0; // 计数器
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.Length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1781,7 +1781,7 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l
|
||||
var count = 0 // 计数器
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in stride(from: nums.count - 1, to: 0, by: -1) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1871,9 +1871,10 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l
|
||||
/* 平方阶(冒泡排序) */
|
||||
fn bubble_sort(nums: &mut [i32]) -> i32 {
|
||||
let mut count = 0; // 计数器
|
||||
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1921,7 +1922,7 @@ For instance, in bubble sort, the outer loop runs $n - 1$ times, and the inner l
|
||||
var i: i32 = @as(i32, @intCast(nums.len)) - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var j: usize = 0;
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -2792,10 +2793,10 @@ Linear-logarithmic order often appears in nested loops, with the complexities of
|
||||
return 1;
|
||||
}
|
||||
let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);
|
||||
for _ in 0 ..n as i32 {
|
||||
for _ in 0..n as i32 {
|
||||
count += 1;
|
||||
}
|
||||
return count
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -469,7 +469,7 @@ The design of hash algorithms is a complex issue that requires consideration of
|
||||
}
|
||||
|
||||
hash as i32
|
||||
}
|
||||
}
|
||||
|
||||
/* 乘法哈希 */
|
||||
fn mul_hash(key: &str) -> i32 {
|
||||
|
@ -1863,129 +1863,113 @@ The code below implements an open addressing (linear probing) hash table with la
|
||||
capacity int // 哈希表容量
|
||||
loadThres float64 // 触发扩容的负载因子阈值
|
||||
extendRatio int // 扩容倍数
|
||||
buckets []pair // 桶数组
|
||||
removed pair // 删除标记
|
||||
buckets []*pair // 桶数组
|
||||
TOMBSTONE *pair // 删除标记
|
||||
}
|
||||
|
||||
/* 构造方法 */
|
||||
func newHashMapOpenAddressing() *hashMapOpenAddressing {
|
||||
buckets := make([]pair, 4)
|
||||
return &hashMapOpenAddressing{
|
||||
size: 0,
|
||||
capacity: 4,
|
||||
loadThres: 2.0 / 3.0,
|
||||
extendRatio: 2,
|
||||
buckets: buckets,
|
||||
removed: pair{
|
||||
key: -1,
|
||||
val: "-1",
|
||||
},
|
||||
buckets: make([]*pair, 4),
|
||||
TOMBSTONE: &pair{-1, "-1"},
|
||||
}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
func (m *hashMapOpenAddressing) hashFunc(key int) int {
|
||||
return key % m.capacity
|
||||
func (h *hashMapOpenAddressing) hashFunc(key int) int {
|
||||
return key % h.capacity // 根据键计算哈希值
|
||||
}
|
||||
|
||||
/* 负载因子 */
|
||||
func (m *hashMapOpenAddressing) loadFactor() float64 {
|
||||
return float64(m.size) / float64(m.capacity)
|
||||
func (h *hashMapOpenAddressing) loadFactor() float64 {
|
||||
return float64(h.size) / float64(h.capacity) // 计算当前负载因子
|
||||
}
|
||||
|
||||
/* 搜索 key 对应的桶索引 */
|
||||
func (h *hashMapOpenAddressing) findBucket(key int) int {
|
||||
index := h.hashFunc(key) // 获取初始索引
|
||||
firstTombstone := -1 // 记录遇到的第一个TOMBSTONE的位置
|
||||
for h.buckets[index] != nil {
|
||||
if h.buckets[index].key == key {
|
||||
if firstTombstone != -1 {
|
||||
// 若之前遇到了删除标记,则将键值对移动至该索引处
|
||||
h.buckets[firstTombstone] = h.buckets[index]
|
||||
h.buckets[index] = h.TOMBSTONE
|
||||
return firstTombstone // 返回移动后的桶索引
|
||||
}
|
||||
return index // 返回找到的索引
|
||||
}
|
||||
if firstTombstone == -1 && h.buckets[index] == h.TOMBSTONE {
|
||||
firstTombstone = index // 记录遇到的首个删除标记的位置
|
||||
}
|
||||
index = (index + 1) % h.capacity // 线性探测,越过尾部则返回头部
|
||||
}
|
||||
// 若 key 不存在,则返回添加点的索引
|
||||
if firstTombstone != -1 {
|
||||
return firstTombstone
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
func (m *hashMapOpenAddressing) get(key int) string {
|
||||
idx := m.hashFunc(key)
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶,说明无此 key ,则返回 null
|
||||
if m.buckets[j] == (pair{}) {
|
||||
return ""
|
||||
}
|
||||
// 若遇到指定 key ,则返回对应 val
|
||||
if m.buckets[j].key == key && m.buckets[j] != m.removed {
|
||||
return m.buckets[j].val
|
||||
}
|
||||
func (h *hashMapOpenAddressing) get(key int) string {
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE {
|
||||
return h.buckets[index].val // 若找到键值对,则返回对应 val
|
||||
}
|
||||
// 若未找到 key ,则返回空字符串
|
||||
return ""
|
||||
return "" // 若键值对不存在,则返回 ""
|
||||
}
|
||||
|
||||
/* 添加操作 */
|
||||
func (m *hashMapOpenAddressing) put(key int, val string) {
|
||||
// 当负载因子超过阈值时,执行扩容
|
||||
if m.loadFactor() > m.loadThres {
|
||||
m.extend()
|
||||
func (h *hashMapOpenAddressing) put(key int, val string) {
|
||||
if h.loadFactor() > h.loadThres {
|
||||
h.extend() // 当负载因子超过阈值时,执行扩容
|
||||
}
|
||||
idx := m.hashFunc(key)
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶
|
||||
if m.buckets[j] == (pair{}) || m.buckets[j] == m.removed {
|
||||
m.buckets[j] = pair{
|
||||
key: key,
|
||||
val: val,
|
||||
}
|
||||
m.size += 1
|
||||
return
|
||||
}
|
||||
// 若遇到指定 key ,则更新对应 val
|
||||
if m.buckets[j].key == key {
|
||||
m.buckets[j].val = val
|
||||
return
|
||||
}
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] == nil || h.buckets[index] == h.TOMBSTONE {
|
||||
h.buckets[index] = &pair{key, val} // 若键值对不存在,则添加该键值对
|
||||
h.size++
|
||||
} else {
|
||||
h.buckets[index].val = val // 若找到键值对,则覆盖 val
|
||||
}
|
||||
}
|
||||
|
||||
/* 删除操作 */
|
||||
func (m *hashMapOpenAddressing) remove(key int) {
|
||||
idx := m.hashFunc(key)
|
||||
// 遍历桶,从中删除键值对
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶,说明无此 key ,则直接返回
|
||||
if m.buckets[j] == (pair{}) {
|
||||
return
|
||||
}
|
||||
// 若遇到指定 key ,则标记删除并返回
|
||||
if m.buckets[j].key == key {
|
||||
m.buckets[j] = m.removed
|
||||
m.size -= 1
|
||||
}
|
||||
func (h *hashMapOpenAddressing) remove(key int) {
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE {
|
||||
h.buckets[index] = h.TOMBSTONE // 若找到键值对,则用删除标记覆盖它
|
||||
h.size--
|
||||
}
|
||||
}
|
||||
|
||||
/* 扩容哈希表 */
|
||||
func (m *hashMapOpenAddressing) extend() {
|
||||
// 暂存原哈希表
|
||||
tmpBuckets := make([]pair, len(m.buckets))
|
||||
copy(tmpBuckets, m.buckets)
|
||||
|
||||
// 初始化扩容后的新哈希表
|
||||
m.capacity *= m.extendRatio
|
||||
m.buckets = make([]pair, m.capacity)
|
||||
m.size = 0
|
||||
func (h *hashMapOpenAddressing) extend() {
|
||||
oldBuckets := h.buckets // 暂存原哈希表
|
||||
h.capacity *= h.extendRatio // 更新容量
|
||||
h.buckets = make([]*pair, h.capacity) // 初始化扩容后的新哈希表
|
||||
h.size = 0 // 重置大小
|
||||
// 将键值对从原哈希表搬运至新哈希表
|
||||
for _, p := range tmpBuckets {
|
||||
if p != (pair{}) && p != m.removed {
|
||||
m.put(p.key, p.val)
|
||||
for _, pair := range oldBuckets {
|
||||
if pair != nil && pair != h.TOMBSTONE {
|
||||
h.put(pair.key, pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 打印哈希表 */
|
||||
func (m *hashMapOpenAddressing) print() {
|
||||
for _, p := range m.buckets {
|
||||
if p != (pair{}) {
|
||||
fmt.Println(strconv.Itoa(p.key) + " -> " + p.val)
|
||||
} else {
|
||||
func (h *hashMapOpenAddressing) print() {
|
||||
for _, pair := range h.buckets {
|
||||
if pair == nil {
|
||||
fmt.Println("nil")
|
||||
} else if pair == h.TOMBSTONE {
|
||||
fmt.Println("TOMBSTONE")
|
||||
} else {
|
||||
fmt.Printf("%d -> %s\n", pair.key, pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2540,15 +2524,14 @@ The code below implements an open addressing (linear probing) hash table with la
|
||||
```rust title="hash_map_open_addressing.rs"
|
||||
/* 开放寻址哈希表 */
|
||||
struct HashMapOpenAddressing {
|
||||
size: usize, // 键值对数量
|
||||
capacity: usize, // 哈希表容量
|
||||
load_thres: f64, // 触发扩容的负载因子阈值
|
||||
extend_ratio: usize, // 扩容倍数
|
||||
buckets: Vec<Option<Pair>>, // 桶数组
|
||||
TOMBSTONE: Option<Pair>, // 删除标记
|
||||
size: usize, // 键值对数量
|
||||
capacity: usize, // 哈希表容量
|
||||
load_thres: f64, // 触发扩容的负载因子阈值
|
||||
extend_ratio: usize, // 扩容倍数
|
||||
buckets: Vec<Option<Pair>>, // 桶数组
|
||||
TOMBSTONE: Option<Pair>, // 删除标记
|
||||
}
|
||||
|
||||
|
||||
impl HashMapOpenAddressing {
|
||||
/* 构造方法 */
|
||||
fn new() -> Self {
|
||||
@ -2558,7 +2541,10 @@ The code below implements an open addressing (linear probing) hash table with la
|
||||
load_thres: 2.0 / 3.0,
|
||||
extend_ratio: 2,
|
||||
buckets: vec![None; 4],
|
||||
TOMBSTONE: Some(Pair {key: -1, val: "-1".to_string()}),
|
||||
TOMBSTONE: Some(Pair {
|
||||
key: -1,
|
||||
val: "-1".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2584,9 +2570,9 @@ The code below implements an open addressing (linear probing) hash table with la
|
||||
if first_tombstone != -1 {
|
||||
self.buckets[first_tombstone as usize] = self.buckets[index].take();
|
||||
self.buckets[index] = self.TOMBSTONE.clone();
|
||||
return first_tombstone as usize; // 返回移动后的桶索引
|
||||
return first_tombstone as usize; // 返回移动后的桶索引
|
||||
}
|
||||
return index; // 返回桶索引
|
||||
return index; // 返回桶索引
|
||||
}
|
||||
// 记录遇到的首个删除标记
|
||||
if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE {
|
||||
@ -2596,7 +2582,11 @@ The code below implements an open addressing (linear probing) hash table with la
|
||||
index = (index + 1) % self.capacity;
|
||||
}
|
||||
// 若 key 不存在,则返回添加点的索引
|
||||
if first_tombstone == -1 { index } else { first_tombstone as usize }
|
||||
if first_tombstone == -1 {
|
||||
index
|
||||
} else {
|
||||
first_tombstone as usize
|
||||
}
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
|
@ -1343,13 +1343,15 @@ The following code implements a simple hash table. Here, we encapsulate `key` an
|
||||
|
||||
/* 基于数组实现的哈希表 */
|
||||
pub struct ArrayHashMap {
|
||||
buckets: Vec<Option<Pair>>
|
||||
buckets: Vec<Option<Pair>>,
|
||||
}
|
||||
|
||||
impl ArrayHashMap {
|
||||
pub fn new() -> ArrayHashMap {
|
||||
// 初始化数组,包含 100 个桶
|
||||
Self { buckets: vec![None; 100] }
|
||||
Self {
|
||||
buckets: vec![None; 100],
|
||||
}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
@ -1381,17 +1383,26 @@ The following code implements a simple hash table. Here, we encapsulate `key` an
|
||||
|
||||
/* 获取所有键值对 */
|
||||
pub fn entry_set(&self) -> Vec<&Pair> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref()).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 获取所有键 */
|
||||
pub fn key_set(&self) -> Vec<&i32> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.key)).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 获取所有值 */
|
||||
pub fn value_set(&self) -> Vec<&String> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.val)).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.val))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 打印哈希表 */
|
||||
|
@ -1522,9 +1522,9 @@ The implementation code is as follows:
|
||||
/* 基于双向链表实现的双向队列 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListDeque<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 双向队列的长度
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 双向队列的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListDeque<T> {
|
||||
@ -1532,7 +1532,7 @@ The implementation code is as follows:
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1564,7 +1564,7 @@ The implementation code is as follows:
|
||||
self.front = Some(node); // 更新头节点
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 队尾入队操作
|
||||
else {
|
||||
match self.rear.take() {
|
||||
@ -1597,8 +1597,8 @@ The implementation code is as follows:
|
||||
/* 出队操作 */
|
||||
pub fn pop(&mut self, is_front: bool) -> Option<T> {
|
||||
// 若队列为空,直接返回 None
|
||||
if self.is_empty() {
|
||||
return None
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
};
|
||||
// 队首出队操作
|
||||
if is_front {
|
||||
@ -1606,7 +1606,7 @@ The implementation code is as follows:
|
||||
match old_front.borrow_mut().next.take() {
|
||||
Some(new_front) => {
|
||||
new_front.borrow_mut().prev.take();
|
||||
self.front = Some(new_front); // 更新头节点
|
||||
self.front = Some(new_front); // 更新头节点
|
||||
}
|
||||
None => {
|
||||
self.rear.take();
|
||||
@ -1615,15 +1615,14 @@ The implementation code is as follows:
|
||||
self.que_size -= 1; // 更新队列长度
|
||||
Rc::try_unwrap(old_front).ok().unwrap().into_inner().val
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
// 队尾出队操作
|
||||
else {
|
||||
self.rear.take().map(|old_rear| {
|
||||
match old_rear.borrow_mut().prev.take() {
|
||||
Some(new_rear) => {
|
||||
new_rear.borrow_mut().next.take();
|
||||
self.rear = Some(new_rear); // 更新尾节点
|
||||
self.rear = Some(new_rear); // 更新尾节点
|
||||
}
|
||||
None => {
|
||||
self.front.take();
|
||||
|
@ -959,9 +959,9 @@ Below is the code for implementing a queue using a linked list:
|
||||
/* 基于链表实现的队列 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListQueue<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 队列的长度
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 队列的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListQueue<T> {
|
||||
@ -969,7 +969,7 @@ Below is the code for implementing a queue using a linked list:
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1887,10 +1887,10 @@ For a circular array, `front` or `rear` needs to loop back to the start of the a
|
||||
```rust title="array_queue.rs"
|
||||
/* 基于环形数组实现的队列 */
|
||||
struct ArrayQueue {
|
||||
nums: Vec<i32>, // 用于存储队列元素的数组
|
||||
front: i32, // 队首指针,指向队首元素
|
||||
que_size: i32, // 队列长度
|
||||
que_capacity: i32, // 队列容量
|
||||
nums: Vec<i32>, // 用于存储队列元素的数组
|
||||
front: i32, // 队首指针,指向队首元素
|
||||
que_size: i32, // 队列长度
|
||||
que_capacity: i32, // 队列容量
|
||||
}
|
||||
|
||||
impl ArrayQueue {
|
||||
|
@ -876,8 +876,8 @@ Below is an example code for implementing a stack based on a linked list:
|
||||
/* 基于链表实现的栈 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListStack<T> {
|
||||
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // 将头节点作为栈顶
|
||||
stk_size: usize, // 栈的长度
|
||||
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // 将头节点作为栈顶
|
||||
stk_size: usize, // 栈的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListStack<T> {
|
||||
@ -1537,7 +1537,9 @@ Since the elements to be pushed onto the stack may continuously increase, we can
|
||||
impl<T> ArrayStack<T> {
|
||||
/* 初始化栈 */
|
||||
fn new() -> ArrayStack<T> {
|
||||
ArrayStack::<T> { stack: Vec::<T>::new() }
|
||||
ArrayStack::<T> {
|
||||
stack: Vec::<T>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取栈的长度 */
|
||||
@ -1565,7 +1567,9 @@ Since the elements to be pushed onto the stack may continuously increase, we can
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
fn peek(&self) -> Option<&T> {
|
||||
if self.is_empty() { panic!("栈为空") };
|
||||
if self.is_empty() {
|
||||
panic!("栈为空")
|
||||
};
|
||||
self.stack.last()
|
||||
}
|
||||
|
||||
|
@ -517,7 +517,7 @@ comments: true
|
||||
/* 在链表的节点 n0 之后插入节点 P */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn insert<T>(n0: &Rc<RefCell<ListNode<T>>>, P: Rc<RefCell<ListNode<T>>>) {
|
||||
let n1 = n0.borrow_mut().next.take();
|
||||
let n1 = n0.borrow_mut().next.take();
|
||||
P.borrow_mut().next = n1;
|
||||
n0.borrow_mut().next = Some(P);
|
||||
}
|
||||
@ -695,7 +695,9 @@ comments: true
|
||||
/* 删除链表的节点 n0 之后的首个节点 */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn remove<T>(n0: &Rc<RefCell<ListNode<T>>>) {
|
||||
if n0.borrow().next.is_none() {return};
|
||||
if n0.borrow().next.is_none() {
|
||||
return;
|
||||
};
|
||||
// n0 -> P -> n1
|
||||
let P = n0.borrow_mut().next.take();
|
||||
if let Some(node) = P {
|
||||
@ -877,10 +879,13 @@ comments: true
|
||||
```rust title="linked_list.rs"
|
||||
/* 访问链表中索引为 index 的节点 */
|
||||
pub fn access<T>(head: Rc<RefCell<ListNode<T>>>, index: i32) -> Rc<RefCell<ListNode<T>>> {
|
||||
if index <= 0 {return head};
|
||||
if let Some(node) = &head.borrow_mut().next {
|
||||
if index <= 0 {
|
||||
return head;
|
||||
};
|
||||
if let Some(node) = &head.borrow().next {
|
||||
return access(node.clone(), index - 1);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
```
|
||||
@ -1076,7 +1081,9 @@ comments: true
|
||||
```rust title="linked_list.rs"
|
||||
/* 在链表中查找值为 target 的首个节点 */
|
||||
pub fn find<T: PartialEq>(head: Rc<RefCell<ListNode<T>>>, target: T, index: i32) -> i32 {
|
||||
if head.borrow().val == target {return index};
|
||||
if head.borrow().val == target {
|
||||
return index;
|
||||
};
|
||||
if let Some(node) = &head.borrow_mut().next {
|
||||
return find(node.clone(), target, index + 1);
|
||||
}
|
||||
|
@ -1815,16 +1815,16 @@ comments: true
|
||||
#[allow(dead_code)]
|
||||
struct MyList {
|
||||
arr: Vec<i32>, // 数组(存储列表元素)
|
||||
capacity: usize, // 列表容量
|
||||
size: usize, // 列表长度(当前元素数量)
|
||||
extend_ratio: usize, // 每次列表扩容的倍数
|
||||
capacity: usize, // 列表容量
|
||||
size: usize, // 列表长度(当前元素数量)
|
||||
extend_ratio: usize, // 每次列表扩容的倍数
|
||||
}
|
||||
|
||||
#[allow(unused,unused_comparisons)]
|
||||
#[allow(unused, unused_comparisons)]
|
||||
impl MyList {
|
||||
/* 构造方法 */
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
let mut vec = Vec::new();
|
||||
let mut vec = Vec::new();
|
||||
vec.resize(capacity, 0);
|
||||
Self {
|
||||
arr: vec,
|
||||
@ -1847,13 +1847,17 @@ comments: true
|
||||
/* 访问元素 */
|
||||
pub fn get(&self, index: usize) -> i32 {
|
||||
// 索引如果越界,则抛出异常,下同
|
||||
if index >= self.size {panic!("索引越界")};
|
||||
if index >= self.size {
|
||||
panic!("索引越界")
|
||||
};
|
||||
return self.arr[index];
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
pub fn set(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size {panic!("索引越界")};
|
||||
if index >= self.size {
|
||||
panic!("索引越界")
|
||||
};
|
||||
self.arr[index] = num;
|
||||
}
|
||||
|
||||
@ -1870,7 +1874,9 @@ comments: true
|
||||
|
||||
/* 在中间插入元素 */
|
||||
pub fn insert(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size() {panic!("索引越界")};
|
||||
if index >= self.size() {
|
||||
panic!("索引越界")
|
||||
};
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if self.size == self.capacity() {
|
||||
self.extend_capacity();
|
||||
@ -1886,7 +1892,9 @@ comments: true
|
||||
|
||||
/* 删除元素 */
|
||||
pub fn remove(&mut self, index: usize) -> i32 {
|
||||
if index >= self.size() {panic!("索引越界")};
|
||||
if index >= self.size() {
|
||||
panic!("索引越界")
|
||||
};
|
||||
let num = self.arr[index];
|
||||
// 将将索引 index 之后的元素都向前移动一位
|
||||
for j in (index..self.size - 1) {
|
||||
|
@ -35,11 +35,11 @@ comments: true
|
||||
# 元素内存地址 = 数组内存地址(首元素内存地址) + 元素长度 * 元素索引
|
||||
```
|
||||
|
||||
**Q**:删除节点后,是否需要把 `P.next` 设为 `None` 呢?
|
||||
**Q**:删除节点 `P` 后,是否需要把 `P.next` 设为 `None` 呢?
|
||||
|
||||
不修改 `P.next` 也可以。从该链表的角度看,从头节点遍历到尾节点已经不会遇到 `P` 了。这意味着节点 `P` 已经从链表中删除了,此时节点 `P` 指向哪里都不会对该链表产生影响。
|
||||
|
||||
从垃圾回收的角度看,对于 Java、Python、Go 等拥有自动垃圾回收机制的语言来说,节点 `P` 是否被回收取决于是否仍存在指向它的引用,而不是 `P.next` 的值。在 C 和 C++ 等语言中,我们需要手动释放节点内存。
|
||||
从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它会影响后继节点的内存回收。
|
||||
|
||||
**Q**:在链表中插入和删除操作的时间复杂度是 $O(1)$ 。但是增删之前都需要 $O(n)$ 的时间查找元素,那为什么时间复杂度不是 $O(n)$ 呢?
|
||||
|
||||
@ -78,7 +78,3 @@ comments: true
|
||||
**Q**:初始化列表 `res = [0] * self.size()` 操作,会导致 `res` 的每个元素引用相同的地址吗?
|
||||
|
||||
不会。但二维数组会有这个问题,例如初始化二维列表 `res = [[0] * self.size()]` ,则多次引用了同一个列表 `[0]` 。
|
||||
|
||||
**Q**:在删除节点中,需要断开该节点与其后继节点之间的引用指向吗?
|
||||
|
||||
从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它会影响后继节点的内存回收。
|
||||
|
@ -428,7 +428,11 @@ comments: true
|
||||
|
||||
```rust title="preorder_traversal_ii_compact.rs"
|
||||
/* 前序遍历:例题二 */
|
||||
fn pre_order(res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>, path: &mut Vec<Rc<RefCell<TreeNode>>>, root: Option<Rc<RefCell<TreeNode>>>) {
|
||||
fn pre_order(
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
path: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
root: Option<Rc<RefCell<TreeNode>>>,
|
||||
) {
|
||||
if root.is_none() {
|
||||
return;
|
||||
}
|
||||
@ -442,7 +446,7 @@ comments: true
|
||||
pre_order(res, path, node.borrow().left.clone());
|
||||
pre_order(res, path, node.borrow().right.clone());
|
||||
// 回退
|
||||
path.remove(path.len() - 1);
|
||||
path.remove(path.len() - 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -738,7 +742,11 @@ comments: true
|
||||
|
||||
```rust title="preorder_traversal_iii_compact.rs"
|
||||
/* 前序遍历:例题三 */
|
||||
fn pre_order(res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>, path: &mut Vec<Rc<RefCell<TreeNode>>>, root: Option<Rc<RefCell<TreeNode>>>) {
|
||||
fn pre_order(
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
path: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
root: Option<Rc<RefCell<TreeNode>>>,
|
||||
) {
|
||||
// 剪枝
|
||||
if root.is_none() || root.as_ref().unwrap().borrow().val == 3 {
|
||||
return;
|
||||
@ -753,7 +761,7 @@ comments: true
|
||||
pre_order(res, path, node.borrow().left.clone());
|
||||
pre_order(res, path, node.borrow().right.clone());
|
||||
// 回退
|
||||
path.remove(path.len() - 1);
|
||||
path.remove(path.len() - 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -1558,7 +1566,10 @@ comments: true
|
||||
}
|
||||
|
||||
/* 记录解 */
|
||||
fn record_solution(state: &mut Vec<Rc<RefCell<TreeNode>>>, res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>) {
|
||||
fn record_solution(
|
||||
state: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
) {
|
||||
res.push(state.clone());
|
||||
}
|
||||
|
||||
@ -1578,7 +1589,11 @@ comments: true
|
||||
}
|
||||
|
||||
/* 回溯算法:例题三 */
|
||||
fn backtrack(state: &mut Vec<Rc<RefCell<TreeNode>>>, choices: &mut Vec<Rc<RefCell<TreeNode>>>, res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>) {
|
||||
fn backtrack(
|
||||
state: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
choices: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
) {
|
||||
// 检查是否为解
|
||||
if is_solution(state) {
|
||||
// 记录解
|
||||
@ -1591,7 +1606,14 @@ comments: true
|
||||
// 尝试:做出选择,更新状态
|
||||
make_choice(state, choice.clone());
|
||||
// 进行下一轮选择
|
||||
backtrack(state, &mut vec![choice.borrow().left.clone().unwrap(), choice.borrow().right.clone().unwrap()], res);
|
||||
backtrack(
|
||||
state,
|
||||
&mut vec![
|
||||
choice.borrow().left.clone().unwrap(),
|
||||
choice.borrow().right.clone().unwrap(),
|
||||
],
|
||||
res,
|
||||
);
|
||||
// 回退:撤销选择,恢复到之前的状态
|
||||
undo_choice(state, choice.clone());
|
||||
}
|
||||
|
@ -516,8 +516,15 @@ comments: true
|
||||
|
||||
```rust title="n_queens.rs"
|
||||
/* 回溯算法:n 皇后 */
|
||||
fn backtrack(row: usize, n: usize, state: &mut Vec<Vec<String>>, res: &mut Vec<Vec<Vec<String>>>,
|
||||
cols: &mut [bool], diags1: &mut [bool], diags2: &mut [bool]) {
|
||||
fn backtrack(
|
||||
row: usize,
|
||||
n: usize,
|
||||
state: &mut Vec<Vec<String>>,
|
||||
res: &mut Vec<Vec<Vec<String>>>,
|
||||
cols: &mut [bool],
|
||||
diags1: &mut [bool],
|
||||
diags2: &mut [bool],
|
||||
) {
|
||||
// 当放置完所有行时,记录解
|
||||
if row == n {
|
||||
let mut copy_state: Vec<Vec<String>> = Vec::new();
|
||||
@ -562,7 +569,15 @@ comments: true
|
||||
let mut diags2 = vec![false; 2 * n - 1]; // 记录次对角线上是否有皇后
|
||||
let mut res: Vec<Vec<Vec<String>>> = Vec::new();
|
||||
|
||||
backtrack(0, n, &mut state, &mut res, &mut cols, &mut diags1, &mut diags2);
|
||||
backtrack(
|
||||
0,
|
||||
n,
|
||||
&mut state,
|
||||
&mut res,
|
||||
&mut cols,
|
||||
&mut diags1,
|
||||
&mut diags2,
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
|
@ -354,7 +354,13 @@ comments: true
|
||||
|
||||
```rust title="subset_sum_i_naive.rs"
|
||||
/* 回溯算法:子集和 I */
|
||||
fn backtrack(mut state: Vec<i32>, target: i32, total: i32, choices: &[i32], res: &mut Vec<Vec<i32>>) {
|
||||
fn backtrack(
|
||||
mut state: Vec<i32>,
|
||||
target: i32,
|
||||
total: i32,
|
||||
choices: &[i32],
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// 子集和等于 target 时,记录解
|
||||
if total == target {
|
||||
res.push(state);
|
||||
@ -830,7 +836,13 @@ comments: true
|
||||
|
||||
```rust title="subset_sum_i.rs"
|
||||
/* 回溯算法:子集和 I */
|
||||
fn backtrack(mut state: Vec<i32>, target: i32, choices: &[i32], start: usize, res: &mut Vec<Vec<i32>>) {
|
||||
fn backtrack(
|
||||
mut state: Vec<i32>,
|
||||
target: i32,
|
||||
choices: &[i32],
|
||||
start: usize,
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// 子集和等于 target 时,记录解
|
||||
if target == 0 {
|
||||
res.push(state);
|
||||
@ -1344,7 +1356,13 @@ comments: true
|
||||
|
||||
```rust title="subset_sum_ii.rs"
|
||||
/* 回溯算法:子集和 II */
|
||||
fn backtrack(mut state: Vec<i32>, target: i32, choices: &[i32], start: usize, res: &mut Vec<Vec<i32>>) {
|
||||
fn backtrack(
|
||||
mut state: Vec<i32>,
|
||||
target: i32,
|
||||
choices: &[i32],
|
||||
start: usize,
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// 子集和等于 target 时,记录解
|
||||
if target == 0 {
|
||||
res.push(state);
|
||||
|
@ -151,7 +151,7 @@ comments: true
|
||||
res += i;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -352,6 +352,7 @@ comments: true
|
||||
fn while_loop(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // 初始化条件变量
|
||||
|
||||
// 循环求和 1, 2, ..., n-1, n
|
||||
while i <= n {
|
||||
res += i;
|
||||
@ -570,6 +571,7 @@ comments: true
|
||||
fn while_loop_ii(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // 初始化条件变量
|
||||
|
||||
// 循环求和 1, 4, 10, ...
|
||||
while i <= n {
|
||||
res += i;
|
||||
|
@ -978,7 +978,7 @@ $$
|
||||
|
||||
```rust title="space_complexity.rs"
|
||||
/* 函数 */
|
||||
fn function() ->i32 {
|
||||
fn function() -> i32 {
|
||||
// 执行某些操作
|
||||
return 0;
|
||||
}
|
||||
@ -1451,7 +1451,9 @@ $$
|
||||
/* 线性阶(递归实现) */
|
||||
fn linear_recur(n: i32) {
|
||||
println!("递归 n = {}", n);
|
||||
if n == 1 {return};
|
||||
if n == 1 {
|
||||
return;
|
||||
};
|
||||
linear_recur(n - 1);
|
||||
}
|
||||
```
|
||||
@ -1833,7 +1835,9 @@ $$
|
||||
```rust title="space_complexity.rs"
|
||||
/* 平方阶(递归实现) */
|
||||
fn quadratic_recur(n: i32) -> i32 {
|
||||
if n <= 0 {return 0};
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
};
|
||||
// 数组 nums 长度为 n, n-1, ..., 2, 1
|
||||
let nums = vec![0; n as usize];
|
||||
println!("递归 n = {} 中的 nums 长度 = {}", n, nums.len());
|
||||
@ -2010,7 +2014,9 @@ $$
|
||||
```rust title="space_complexity.rs"
|
||||
/* 指数阶(建立满二叉树) */
|
||||
fn build_tree(n: i32) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
if n == 0 {return None};
|
||||
if n == 0 {
|
||||
return None;
|
||||
};
|
||||
let root = TreeNode::new(0);
|
||||
root.borrow_mut().left = build_tree(n - 1);
|
||||
root.borrow_mut().right = build_tree(n - 1);
|
||||
|
@ -1741,7 +1741,7 @@ $$
|
||||
int count = 0; // 计数器
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.Length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1785,7 +1785,7 @@ $$
|
||||
var count = 0 // 计数器
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in stride(from: nums.count - 1, to: 0, by: -1) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1875,9 +1875,10 @@ $$
|
||||
/* 平方阶(冒泡排序) */
|
||||
fn bubble_sort(nums: &mut [i32]) -> i32 {
|
||||
let mut count = 0; // 计数器
|
||||
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -1925,7 +1926,7 @@ $$
|
||||
var i: i32 = @as(i32, @intCast(nums.len)) - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var j: usize = 0;
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -2796,10 +2797,10 @@ $$
|
||||
return 1;
|
||||
}
|
||||
let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);
|
||||
for _ in 0 ..n as i32 {
|
||||
for _ in 0..n as i32 {
|
||||
count += 1;
|
||||
}
|
||||
return count
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -329,7 +329,9 @@ comments: true
|
||||
/* 二分查找:问题 f(i, j) */
|
||||
fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 {
|
||||
// 若区间为空,代表无目标元素,则返回 -1
|
||||
if i > j { return -1; }
|
||||
if i > j {
|
||||
return -1;
|
||||
}
|
||||
let m: i32 = (i + j) / 2;
|
||||
if nums[m as usize] < target {
|
||||
// 递归子问题 f(m+1, j)
|
||||
|
@ -374,9 +374,17 @@ comments: true
|
||||
|
||||
```rust title="build_tree.rs"
|
||||
/* 构建二叉树:分治 */
|
||||
fn dfs(preorder: &[i32], inorder_map: &HashMap<i32, i32>, i: i32, l: i32, r: i32) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
fn dfs(
|
||||
preorder: &[i32],
|
||||
inorder_map: &HashMap<i32, i32>,
|
||||
i: i32,
|
||||
l: i32,
|
||||
r: i32,
|
||||
) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
// 子树区间为空时终止
|
||||
if r - l < 0 { return None; }
|
||||
if r - l < 0 {
|
||||
return None;
|
||||
}
|
||||
// 初始化根节点
|
||||
let root = TreeNode::new(preorder[i as usize]);
|
||||
// 查询 m ,从而划分左右子树
|
||||
|
@ -241,7 +241,9 @@ $$
|
||||
/* 爬楼梯最小代价:动态规划 */
|
||||
fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 {
|
||||
let n = cost.len() - 1;
|
||||
if n == 1 || n == 2 { return cost[n]; }
|
||||
if n == 1 || n == 2 {
|
||||
return cost[n];
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
let mut dp = vec![-1; n + 1];
|
||||
// 初始状态:预设最小子问题的解
|
||||
@ -489,7 +491,9 @@ $$
|
||||
/* 爬楼梯最小代价:空间优化后的动态规划 */
|
||||
fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 {
|
||||
let n = cost.len() - 1;
|
||||
if n == 1 || n == 2 { return cost[n] };
|
||||
if n == 1 || n == 2 {
|
||||
return cost[n];
|
||||
};
|
||||
let (mut a, mut b) = (cost[1], cost[2]);
|
||||
for i in 3..=n {
|
||||
let tmp = b;
|
||||
@ -802,7 +806,9 @@ $$
|
||||
```rust title="climbing_stairs_constraint_dp.rs"
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
fn climbing_stairs_constraint_dp(n: usize) -> i32 {
|
||||
if n == 1 || n == 2 { return 1 };
|
||||
if n == 1 || n == 2 {
|
||||
return 1;
|
||||
};
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
let mut dp = vec![vec![-1; 3]; n + 1];
|
||||
// 初始状态:预设最小子问题的解
|
||||
|
@ -360,7 +360,7 @@ $$
|
||||
let (n, m) = (s.len(), t.len());
|
||||
let mut dp = vec![vec![0; m + 1]; n + 1];
|
||||
// 状态转移:首行首列
|
||||
for i in 1..= n {
|
||||
for i in 1..=n {
|
||||
dp[i][0] = i as i32;
|
||||
}
|
||||
for j in 1..m {
|
||||
@ -374,7 +374,8 @@ $$
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[i][j] = std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
|
||||
dp[i][j] =
|
||||
std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,11 +296,15 @@ comments: true
|
||||
/* 回溯 */
|
||||
fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) {
|
||||
// 当爬到第 n 阶时,方案数量加 1
|
||||
if state == n { res[0] = res[0] + 1; }
|
||||
if state == n {
|
||||
res[0] = res[0] + 1;
|
||||
}
|
||||
// 遍历所有选择
|
||||
for &choice in choices {
|
||||
// 剪枝:不允许越过第 n 阶
|
||||
if state + choice > n { continue; }
|
||||
if state + choice > n {
|
||||
continue;
|
||||
}
|
||||
// 尝试:做出选择,更新状态
|
||||
backtrack(choices, state + choice, n, res);
|
||||
// 回退
|
||||
@ -309,7 +313,7 @@ comments: true
|
||||
|
||||
/* 爬楼梯:回溯 */
|
||||
fn climbing_stairs_backtrack(n: usize) -> i32 {
|
||||
let choices = vec![ 1, 2 ]; // 可选择向上爬 1 阶或 2 阶
|
||||
let choices = vec![1, 2]; // 可选择向上爬 1 阶或 2 阶
|
||||
let state = 0; // 从第 0 阶开始爬
|
||||
let mut res = Vec::new();
|
||||
res.push(0); // 使用 res[0] 记录方案数量
|
||||
@ -592,7 +596,9 @@ $$
|
||||
/* 搜索 */
|
||||
fn dfs(i: usize) -> i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if i == 1 || i == 2 { return i as i32; }
|
||||
if i == 1 || i == 2 {
|
||||
return i as i32;
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
let count = dfs(i - 1) + dfs(i - 2);
|
||||
count
|
||||
@ -600,7 +606,7 @@ $$
|
||||
|
||||
/* 爬楼梯:搜索 */
|
||||
fn climbing_stairs_dfs(n: usize) -> i32 {
|
||||
dfs(n)
|
||||
dfs(n)
|
||||
}
|
||||
```
|
||||
|
||||
@ -908,9 +914,13 @@ $$
|
||||
/* 记忆化搜索 */
|
||||
fn dfs(i: usize, mem: &mut [i32]) -> i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if i == 1 || i == 2 { return i as i32; }
|
||||
if i == 1 || i == 2 {
|
||||
return i as i32;
|
||||
}
|
||||
// 若存在记录 dp[i] ,则直接返回之
|
||||
if mem[i] != -1 { return mem[i]; }
|
||||
if mem[i] != -1 {
|
||||
return mem[i];
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
let count = dfs(i - 1, mem) + dfs(i - 2, mem);
|
||||
// 记录 dp[i]
|
||||
@ -1186,7 +1196,9 @@ $$
|
||||
/* 爬楼梯:动态规划 */
|
||||
fn climbing_stairs_dp(n: usize) -> i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if n == 1 || n == 2 { return n as i32; }
|
||||
if n == 1 || n == 2 {
|
||||
return n as i32;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
let mut dp = vec![-1; n + 1];
|
||||
// 初始状态:预设最小子问题的解
|
||||
@ -1420,7 +1432,9 @@ $$
|
||||
```rust title="climbing_stairs_dp.rs"
|
||||
/* 爬楼梯:空间优化后的动态规划 */
|
||||
fn climbing_stairs_dp_comp(n: usize) -> i32 {
|
||||
if n == 1 || n == 2 { return n as i32; }
|
||||
if n == 1 || n == 2 {
|
||||
return n as i32;
|
||||
}
|
||||
let (mut a, mut b) = (1, 2);
|
||||
for _ in 3..=n {
|
||||
let tmp = b;
|
||||
|
@ -919,7 +919,10 @@ $$
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[i][c] = std::cmp::max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1]);
|
||||
dp[i][c] = std::cmp::max(
|
||||
dp[i - 1][c],
|
||||
dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1002,7 +1002,7 @@ $$
|
||||
// 初始化 dp 表
|
||||
let mut dp = vec![vec![0; amt + 1]; n + 1];
|
||||
// 状态转移:首行首列
|
||||
for a in 1..= amt {
|
||||
for a in 1..=amt {
|
||||
dp[0][a] = max;
|
||||
}
|
||||
// 状态转移:其余行和列
|
||||
@ -1017,7 +1017,11 @@ $$
|
||||
}
|
||||
}
|
||||
}
|
||||
if dp[n][amt] != max { return dp[n][amt] as i32; } else { -1 }
|
||||
if dp[n][amt] != max {
|
||||
return dp[n][amt] as i32;
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -1412,7 +1416,11 @@ $$
|
||||
}
|
||||
}
|
||||
}
|
||||
if dp[amt] != max { return dp[amt] as i32; } else { -1 }
|
||||
if dp[amt] != max {
|
||||
return dp[amt] as i32;
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -1768,7 +1776,7 @@ $$
|
||||
// 初始化 dp 表
|
||||
let mut dp = vec![vec![0; amt + 1]; n + 1];
|
||||
// 初始化首列
|
||||
for i in 0..= n {
|
||||
for i in 0..=n {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// 状态转移
|
||||
|
@ -323,7 +323,8 @@ BFS 通常借助队列来实现,代码如下所示。队列具有“先入先
|
||||
while !que.is_empty() {
|
||||
let vet = que.pop_front().unwrap(); // 队首顶点出队
|
||||
res.push(vet); // 记录访问顶点
|
||||
// 遍历该顶点的所有邻接顶点
|
||||
|
||||
// 遍历该顶点的所有邻接顶点
|
||||
if let Some(adj_vets) = graph.adj_list.get(&vet) {
|
||||
for &adj_vet in adj_vets {
|
||||
if visited.contains(&adj_vet) {
|
||||
|
@ -469,7 +469,7 @@ index = hash(key) % capacity
|
||||
}
|
||||
|
||||
hash as i32
|
||||
}
|
||||
}
|
||||
|
||||
/* 乘法哈希 */
|
||||
fn mul_hash(key: &str) -> i32 {
|
||||
|
@ -1863,129 +1863,113 @@ comments: true
|
||||
capacity int // 哈希表容量
|
||||
loadThres float64 // 触发扩容的负载因子阈值
|
||||
extendRatio int // 扩容倍数
|
||||
buckets []pair // 桶数组
|
||||
removed pair // 删除标记
|
||||
buckets []*pair // 桶数组
|
||||
TOMBSTONE *pair // 删除标记
|
||||
}
|
||||
|
||||
/* 构造方法 */
|
||||
func newHashMapOpenAddressing() *hashMapOpenAddressing {
|
||||
buckets := make([]pair, 4)
|
||||
return &hashMapOpenAddressing{
|
||||
size: 0,
|
||||
capacity: 4,
|
||||
loadThres: 2.0 / 3.0,
|
||||
extendRatio: 2,
|
||||
buckets: buckets,
|
||||
removed: pair{
|
||||
key: -1,
|
||||
val: "-1",
|
||||
},
|
||||
buckets: make([]*pair, 4),
|
||||
TOMBSTONE: &pair{-1, "-1"},
|
||||
}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
func (m *hashMapOpenAddressing) hashFunc(key int) int {
|
||||
return key % m.capacity
|
||||
func (h *hashMapOpenAddressing) hashFunc(key int) int {
|
||||
return key % h.capacity // 根据键计算哈希值
|
||||
}
|
||||
|
||||
/* 负载因子 */
|
||||
func (m *hashMapOpenAddressing) loadFactor() float64 {
|
||||
return float64(m.size) / float64(m.capacity)
|
||||
func (h *hashMapOpenAddressing) loadFactor() float64 {
|
||||
return float64(h.size) / float64(h.capacity) // 计算当前负载因子
|
||||
}
|
||||
|
||||
/* 搜索 key 对应的桶索引 */
|
||||
func (h *hashMapOpenAddressing) findBucket(key int) int {
|
||||
index := h.hashFunc(key) // 获取初始索引
|
||||
firstTombstone := -1 // 记录遇到的第一个TOMBSTONE的位置
|
||||
for h.buckets[index] != nil {
|
||||
if h.buckets[index].key == key {
|
||||
if firstTombstone != -1 {
|
||||
// 若之前遇到了删除标记,则将键值对移动至该索引处
|
||||
h.buckets[firstTombstone] = h.buckets[index]
|
||||
h.buckets[index] = h.TOMBSTONE
|
||||
return firstTombstone // 返回移动后的桶索引
|
||||
}
|
||||
return index // 返回找到的索引
|
||||
}
|
||||
if firstTombstone == -1 && h.buckets[index] == h.TOMBSTONE {
|
||||
firstTombstone = index // 记录遇到的首个删除标记的位置
|
||||
}
|
||||
index = (index + 1) % h.capacity // 线性探测,越过尾部则返回头部
|
||||
}
|
||||
// 若 key 不存在,则返回添加点的索引
|
||||
if firstTombstone != -1 {
|
||||
return firstTombstone
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
func (m *hashMapOpenAddressing) get(key int) string {
|
||||
idx := m.hashFunc(key)
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶,说明无此 key ,则返回 null
|
||||
if m.buckets[j] == (pair{}) {
|
||||
return ""
|
||||
}
|
||||
// 若遇到指定 key ,则返回对应 val
|
||||
if m.buckets[j].key == key && m.buckets[j] != m.removed {
|
||||
return m.buckets[j].val
|
||||
}
|
||||
func (h *hashMapOpenAddressing) get(key int) string {
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE {
|
||||
return h.buckets[index].val // 若找到键值对,则返回对应 val
|
||||
}
|
||||
// 若未找到 key ,则返回空字符串
|
||||
return ""
|
||||
return "" // 若键值对不存在,则返回 ""
|
||||
}
|
||||
|
||||
/* 添加操作 */
|
||||
func (m *hashMapOpenAddressing) put(key int, val string) {
|
||||
// 当负载因子超过阈值时,执行扩容
|
||||
if m.loadFactor() > m.loadThres {
|
||||
m.extend()
|
||||
func (h *hashMapOpenAddressing) put(key int, val string) {
|
||||
if h.loadFactor() > h.loadThres {
|
||||
h.extend() // 当负载因子超过阈值时,执行扩容
|
||||
}
|
||||
idx := m.hashFunc(key)
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶
|
||||
if m.buckets[j] == (pair{}) || m.buckets[j] == m.removed {
|
||||
m.buckets[j] = pair{
|
||||
key: key,
|
||||
val: val,
|
||||
}
|
||||
m.size += 1
|
||||
return
|
||||
}
|
||||
// 若遇到指定 key ,则更新对应 val
|
||||
if m.buckets[j].key == key {
|
||||
m.buckets[j].val = val
|
||||
return
|
||||
}
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] == nil || h.buckets[index] == h.TOMBSTONE {
|
||||
h.buckets[index] = &pair{key, val} // 若键值对不存在,则添加该键值对
|
||||
h.size++
|
||||
} else {
|
||||
h.buckets[index].val = val // 若找到键值对,则覆盖 val
|
||||
}
|
||||
}
|
||||
|
||||
/* 删除操作 */
|
||||
func (m *hashMapOpenAddressing) remove(key int) {
|
||||
idx := m.hashFunc(key)
|
||||
// 遍历桶,从中删除键值对
|
||||
// 线性探测,从 index 开始向后遍历
|
||||
for i := 0; i < m.capacity; i++ {
|
||||
// 计算桶索引,越过尾部则返回头部
|
||||
j := (idx + i) % m.capacity
|
||||
// 若遇到空桶,说明无此 key ,则直接返回
|
||||
if m.buckets[j] == (pair{}) {
|
||||
return
|
||||
}
|
||||
// 若遇到指定 key ,则标记删除并返回
|
||||
if m.buckets[j].key == key {
|
||||
m.buckets[j] = m.removed
|
||||
m.size -= 1
|
||||
}
|
||||
func (h *hashMapOpenAddressing) remove(key int) {
|
||||
index := h.findBucket(key) // 搜索 key 对应的桶索引
|
||||
if h.buckets[index] != nil && h.buckets[index] != h.TOMBSTONE {
|
||||
h.buckets[index] = h.TOMBSTONE // 若找到键值对,则用删除标记覆盖它
|
||||
h.size--
|
||||
}
|
||||
}
|
||||
|
||||
/* 扩容哈希表 */
|
||||
func (m *hashMapOpenAddressing) extend() {
|
||||
// 暂存原哈希表
|
||||
tmpBuckets := make([]pair, len(m.buckets))
|
||||
copy(tmpBuckets, m.buckets)
|
||||
|
||||
// 初始化扩容后的新哈希表
|
||||
m.capacity *= m.extendRatio
|
||||
m.buckets = make([]pair, m.capacity)
|
||||
m.size = 0
|
||||
func (h *hashMapOpenAddressing) extend() {
|
||||
oldBuckets := h.buckets // 暂存原哈希表
|
||||
h.capacity *= h.extendRatio // 更新容量
|
||||
h.buckets = make([]*pair, h.capacity) // 初始化扩容后的新哈希表
|
||||
h.size = 0 // 重置大小
|
||||
// 将键值对从原哈希表搬运至新哈希表
|
||||
for _, p := range tmpBuckets {
|
||||
if p != (pair{}) && p != m.removed {
|
||||
m.put(p.key, p.val)
|
||||
for _, pair := range oldBuckets {
|
||||
if pair != nil && pair != h.TOMBSTONE {
|
||||
h.put(pair.key, pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 打印哈希表 */
|
||||
func (m *hashMapOpenAddressing) print() {
|
||||
for _, p := range m.buckets {
|
||||
if p != (pair{}) {
|
||||
fmt.Println(strconv.Itoa(p.key) + " -> " + p.val)
|
||||
} else {
|
||||
func (h *hashMapOpenAddressing) print() {
|
||||
for _, pair := range h.buckets {
|
||||
if pair == nil {
|
||||
fmt.Println("nil")
|
||||
} else if pair == h.TOMBSTONE {
|
||||
fmt.Println("TOMBSTONE")
|
||||
} else {
|
||||
fmt.Printf("%d -> %s\n", pair.key, pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2540,15 +2524,14 @@ comments: true
|
||||
```rust title="hash_map_open_addressing.rs"
|
||||
/* 开放寻址哈希表 */
|
||||
struct HashMapOpenAddressing {
|
||||
size: usize, // 键值对数量
|
||||
capacity: usize, // 哈希表容量
|
||||
load_thres: f64, // 触发扩容的负载因子阈值
|
||||
extend_ratio: usize, // 扩容倍数
|
||||
buckets: Vec<Option<Pair>>, // 桶数组
|
||||
TOMBSTONE: Option<Pair>, // 删除标记
|
||||
size: usize, // 键值对数量
|
||||
capacity: usize, // 哈希表容量
|
||||
load_thres: f64, // 触发扩容的负载因子阈值
|
||||
extend_ratio: usize, // 扩容倍数
|
||||
buckets: Vec<Option<Pair>>, // 桶数组
|
||||
TOMBSTONE: Option<Pair>, // 删除标记
|
||||
}
|
||||
|
||||
|
||||
impl HashMapOpenAddressing {
|
||||
/* 构造方法 */
|
||||
fn new() -> Self {
|
||||
@ -2558,7 +2541,10 @@ comments: true
|
||||
load_thres: 2.0 / 3.0,
|
||||
extend_ratio: 2,
|
||||
buckets: vec![None; 4],
|
||||
TOMBSTONE: Some(Pair {key: -1, val: "-1".to_string()}),
|
||||
TOMBSTONE: Some(Pair {
|
||||
key: -1,
|
||||
val: "-1".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2584,9 +2570,9 @@ comments: true
|
||||
if first_tombstone != -1 {
|
||||
self.buckets[first_tombstone as usize] = self.buckets[index].take();
|
||||
self.buckets[index] = self.TOMBSTONE.clone();
|
||||
return first_tombstone as usize; // 返回移动后的桶索引
|
||||
return first_tombstone as usize; // 返回移动后的桶索引
|
||||
}
|
||||
return index; // 返回桶索引
|
||||
return index; // 返回桶索引
|
||||
}
|
||||
// 记录遇到的首个删除标记
|
||||
if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE {
|
||||
@ -2596,7 +2582,11 @@ comments: true
|
||||
index = (index + 1) % self.capacity;
|
||||
}
|
||||
// 若 key 不存在,则返回添加点的索引
|
||||
if first_tombstone == -1 { index } else { first_tombstone as usize }
|
||||
if first_tombstone == -1 {
|
||||
index
|
||||
} else {
|
||||
first_tombstone as usize
|
||||
}
|
||||
}
|
||||
|
||||
/* 查询操作 */
|
||||
|
@ -1343,13 +1343,15 @@ index = hash(key) % capacity
|
||||
|
||||
/* 基于数组实现的哈希表 */
|
||||
pub struct ArrayHashMap {
|
||||
buckets: Vec<Option<Pair>>
|
||||
buckets: Vec<Option<Pair>>,
|
||||
}
|
||||
|
||||
impl ArrayHashMap {
|
||||
pub fn new() -> ArrayHashMap {
|
||||
// 初始化数组,包含 100 个桶
|
||||
Self { buckets: vec![None; 100] }
|
||||
Self {
|
||||
buckets: vec![None; 100],
|
||||
}
|
||||
}
|
||||
|
||||
/* 哈希函数 */
|
||||
@ -1381,17 +1383,26 @@ index = hash(key) % capacity
|
||||
|
||||
/* 获取所有键值对 */
|
||||
pub fn entry_set(&self) -> Vec<&Pair> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref()).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 获取所有键 */
|
||||
pub fn key_set(&self) -> Vec<&i32> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.key)).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 获取所有值 */
|
||||
pub fn value_set(&self) -> Vec<&String> {
|
||||
self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.val)).collect()
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.val))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* 打印哈希表 */
|
||||
|
@ -4,7 +4,7 @@ icon: fontawesome/solid/book
|
||||
status: new
|
||||
---
|
||||
|
||||
# 纸质书介绍
|
||||
# 纸质书
|
||||
|
||||
经过长时间的打磨,《Hello 算法》纸质书终于发布了!此时的心情可以用一句诗来形容:
|
||||
|
||||
|
@ -274,14 +274,17 @@ comments: true
|
||||
let mut j = nums.len() as i32 - 1;
|
||||
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target { // 此情况说明 target 在区间 [m+1, j] 中
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target {
|
||||
// 此情况说明 target 在区间 [m+1, j] 中
|
||||
i = m + 1;
|
||||
} else if nums[m as usize] > target { // 此情况说明 target 在区间 [i, m-1] 中
|
||||
} else if nums[m as usize] > target {
|
||||
// 此情况说明 target 在区间 [i, m-1] 中
|
||||
j = m - 1;
|
||||
} else { // 找到目标元素,返回其索引
|
||||
} else {
|
||||
// 找到目标元素,返回其索引
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未找到目标元素,返回 -1
|
||||
return -1;
|
||||
@ -570,14 +573,17 @@ comments: true
|
||||
let mut j = nums.len() as i32;
|
||||
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
|
||||
while i < j {
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target { // 此情况说明 target 在区间 [m+1, j) 中
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target {
|
||||
// 此情况说明 target 在区间 [m+1, j) 中
|
||||
i = m + 1;
|
||||
} else if nums[m as usize] > target { // 此情况说明 target 在区间 [i, m) 中
|
||||
} else if nums[m as usize] > target {
|
||||
// 此情况说明 target 在区间 [i, m) 中
|
||||
j = m;
|
||||
} else { // 找到目标元素,返回其索引
|
||||
} else {
|
||||
// 找到目标元素,返回其索引
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未找到目标元素,返回 -1
|
||||
return -1;
|
||||
|
@ -229,13 +229,13 @@ comments: true
|
||||
```rust title="binary_search_insertion.rs"
|
||||
/* 二分查找插入点(无重复元素) */
|
||||
fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 {
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target {
|
||||
i = m + 1; // target 在区间 [m+1, j] 中
|
||||
i = m + 1; // target 在区间 [m+1, j] 中
|
||||
} else if nums[m as usize] > target {
|
||||
j = m - 1; // target 在区间 [i, m-1] 中
|
||||
j = m - 1; // target 在区间 [i, m-1] 中
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
@ -531,15 +531,15 @@ comments: true
|
||||
```rust title="binary_search_insertion.rs"
|
||||
/* 二分查找插入点(存在重复元素) */
|
||||
pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 {
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
let m = i + (j - i) / 2; // 计算中点索引 m
|
||||
if nums[m as usize] < target {
|
||||
i = m + 1; // target 在区间 [m+1, j] 中
|
||||
i = m + 1; // target 在区间 [m+1, j] 中
|
||||
} else if nums[m as usize] > target {
|
||||
j = m - 1; // target 在区间 [i, m-1] 中
|
||||
j = m - 1; // target 在区间 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
|
||||
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入点 i
|
||||
|
@ -433,7 +433,7 @@ comments: true
|
||||
for (i, num) in nums.iter().enumerate() {
|
||||
match dic.get(&(target - num)) {
|
||||
Some(v) => return Some(vec![*v as i32, i as i32]),
|
||||
None => dic.insert(num, i as i32)
|
||||
None => dic.insert(num, i as i32),
|
||||
};
|
||||
}
|
||||
None
|
||||
|
@ -107,7 +107,7 @@ comments: true
|
||||
void BubbleSort(int[] nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.Length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -143,7 +143,7 @@ comments: true
|
||||
func bubbleSort(nums: inout [Int]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in stride(from: nums.count - 1, to: 0, by: -1) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in stride(from: 0, to: i, by: 1) {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -223,7 +223,7 @@ comments: true
|
||||
fn bubble_sort(nums: &mut [i32]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -264,7 +264,7 @@ comments: true
|
||||
var i: usize = nums.len - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var j: usize = 0;
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -362,7 +362,7 @@ comments: true
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.Length - 1; i > 0; i--) {
|
||||
bool flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
@ -499,17 +499,19 @@ comments: true
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
let mut flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
flag = true; // 记录交换元素
|
||||
flag = true; // 记录交换元素
|
||||
}
|
||||
}
|
||||
if !flag {break}; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
if !flag {
|
||||
break; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -547,7 +549,7 @@ comments: true
|
||||
while (i > 0) : (i -= 1) {
|
||||
var flag = false; // 初始化标志位
|
||||
var j: usize = 0;
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
|
@ -200,13 +200,13 @@ comments: true
|
||||
fn insertion_sort(nums: &mut [i32]) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for i in 1..nums.len() {
|
||||
let (base, mut j) = (nums[i], (i - 1) as i32);
|
||||
let (base, mut j) = (nums[i], (i - 1) as i32);
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while j >= 0 && nums[j as usize] > base {
|
||||
nums[(j + 1) as usize] = nums[j as usize]; // 将 nums[j] 向右移动一位
|
||||
j -= 1;
|
||||
}
|
||||
nums[(j + 1) as usize] = base; // 将 base 赋值到正确位置
|
||||
nums[(j + 1) as usize] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -521,11 +521,15 @@ comments: true
|
||||
/* 归并排序 */
|
||||
fn merge_sort(nums: &mut [i32], left: usize, right: usize) {
|
||||
// 终止条件
|
||||
if left >= right { return; } // 当子数组长度为 1 时终止递归
|
||||
if left >= right {
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
}
|
||||
|
||||
// 划分阶段
|
||||
let mid = (left + right) / 2; // 计算中点
|
||||
merge_sort(nums, left, mid); // 递归左子数组
|
||||
merge_sort(nums, mid + 1, right); // 递归右子数组
|
||||
let mid = (left + right) / 2; // 计算中点
|
||||
merge_sort(nums, left, mid); // 递归左子数组
|
||||
merge_sort(nums, mid + 1, right); // 递归右子数组
|
||||
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
|
@ -287,15 +287,15 @@ comments: true
|
||||
let (mut i, mut j) = (left, right);
|
||||
while i < j {
|
||||
while i < j && nums[j] >= nums[left] {
|
||||
j -= 1; // 从右向左找首个小于基准数的元素
|
||||
j -= 1; // 从右向左找首个小于基准数的元素
|
||||
}
|
||||
while i < j && nums[i] <= nums[left] {
|
||||
i += 1; // 从左向右找首个大于基准数的元素
|
||||
i += 1; // 从左向右找首个大于基准数的元素
|
||||
}
|
||||
nums.swap(i, j); // 交换这两个元素
|
||||
}
|
||||
nums.swap(i, left); // 将基准数交换至两子数组的分界线
|
||||
i // 返回基准数的索引
|
||||
nums.swap(i, left); // 将基准数交换至两子数组的分界线
|
||||
i // 返回基准数的索引
|
||||
}
|
||||
```
|
||||
|
||||
@ -942,11 +942,11 @@ comments: true
|
||||
```rust title="quick_sort.rs"
|
||||
/* 选取三个候选元素的中位数 */
|
||||
fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize {
|
||||
let (mut l, mut m, mut r) = (nums[left], nums[mid], nums[right]);
|
||||
if ((l <= m && m <= r) || (r <= m && m <= l)) {
|
||||
let (l, m, r) = (nums[left], nums[mid], nums[right]);
|
||||
if (l <= m && m <= r) || (r <= m && m <= l) {
|
||||
return mid; // m 在 l 和 r 之间
|
||||
}
|
||||
if ((m <= l && l <= r) || (r <= l && l <= m)) {
|
||||
if (m <= l && l <= r) || (r <= l && l <= m) {
|
||||
return left; // l 在 m 和 r 之间
|
||||
}
|
||||
right
|
||||
@ -962,15 +962,15 @@ comments: true
|
||||
let (mut i, mut j) = (left, right);
|
||||
while i < j {
|
||||
while i < j && nums[j] >= nums[left] {
|
||||
j -= 1; // 从右向左找首个小于基准数的元素
|
||||
j -= 1; // 从右向左找首个小于基准数的元素
|
||||
}
|
||||
while i < j && nums[i] <= nums[left] {
|
||||
i += 1; // 从左向右找首个大于基准数的元素
|
||||
i += 1; // 从左向右找首个大于基准数的元素
|
||||
}
|
||||
nums.swap(i, j); // 交换这两个元素
|
||||
}
|
||||
nums.swap(i, left); // 将基准数交换至两子数组的分界线
|
||||
i // 返回基准数的索引
|
||||
nums.swap(i, left); // 将基准数交换至两子数组的分界线
|
||||
i // 返回基准数的索引
|
||||
}
|
||||
```
|
||||
|
||||
@ -1251,9 +1251,9 @@ comments: true
|
||||
// 哨兵划分操作
|
||||
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
|
||||
// 对两个子数组中较短的那个执行快速排序
|
||||
if pivot - left < right - pivot {
|
||||
Self::quick_sort(left, pivot - 1, nums); // 递归排序左子数组
|
||||
left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right]
|
||||
if pivot - left < right - pivot {
|
||||
Self::quick_sort(left, pivot - 1, nums); // 递归排序左子数组
|
||||
left = pivot + 1; // 剩余未排序区间为 [pivot + 1, right]
|
||||
} else {
|
||||
Self::quick_sort(pivot + 1, right, nums); // 递归排序右子数组
|
||||
right = pivot - 1; // 剩余未排序区间为 [left, pivot - 1]
|
||||
|
@ -246,10 +246,10 @@ comments: true
|
||||
}
|
||||
let n = nums.len();
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for i in 0..n-1 {
|
||||
for i in 0..n - 1 {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
let mut k = i;
|
||||
for j in i+1..n {
|
||||
for j in i + 1..n {
|
||||
if nums[j] < nums[k] {
|
||||
k = j; // 记录最小元素的索引
|
||||
}
|
||||
|
@ -1523,9 +1523,9 @@ comments: true
|
||||
/* 基于双向链表实现的双向队列 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListDeque<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 双向队列的长度
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 双向队列的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListDeque<T> {
|
||||
@ -1533,7 +1533,7 @@ comments: true
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1565,7 +1565,7 @@ comments: true
|
||||
self.front = Some(node); // 更新头节点
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 队尾入队操作
|
||||
else {
|
||||
match self.rear.take() {
|
||||
@ -1598,8 +1598,8 @@ comments: true
|
||||
/* 出队操作 */
|
||||
pub fn pop(&mut self, is_front: bool) -> Option<T> {
|
||||
// 若队列为空,直接返回 None
|
||||
if self.is_empty() {
|
||||
return None
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
};
|
||||
// 队首出队操作
|
||||
if is_front {
|
||||
@ -1607,7 +1607,7 @@ comments: true
|
||||
match old_front.borrow_mut().next.take() {
|
||||
Some(new_front) => {
|
||||
new_front.borrow_mut().prev.take();
|
||||
self.front = Some(new_front); // 更新头节点
|
||||
self.front = Some(new_front); // 更新头节点
|
||||
}
|
||||
None => {
|
||||
self.rear.take();
|
||||
@ -1616,15 +1616,14 @@ comments: true
|
||||
self.que_size -= 1; // 更新队列长度
|
||||
Rc::try_unwrap(old_front).ok().unwrap().into_inner().val
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
// 队尾出队操作
|
||||
else {
|
||||
self.rear.take().map(|old_rear| {
|
||||
match old_rear.borrow_mut().prev.take() {
|
||||
Some(new_rear) => {
|
||||
new_rear.borrow_mut().next.take();
|
||||
self.rear = Some(new_rear); // 更新尾节点
|
||||
self.rear = Some(new_rear); // 更新尾节点
|
||||
}
|
||||
None => {
|
||||
self.front.take();
|
||||
@ -2990,9 +2989,9 @@ comments: true
|
||||
```rust title="array_deque.rs"
|
||||
/* 基于环形数组实现的双向队列 */
|
||||
struct ArrayDeque {
|
||||
nums: Vec<i32>, // 用于存储双向队列元素的数组
|
||||
front: usize, // 队首指针,指向队首元素
|
||||
que_size: usize, // 双向队列长度
|
||||
nums: Vec<i32>, // 用于存储双向队列元素的数组
|
||||
front: usize, // 队首指针,指向队首元素
|
||||
que_size: usize, // 双向队列长度
|
||||
}
|
||||
|
||||
impl ArrayDeque {
|
||||
@ -3032,7 +3031,7 @@ comments: true
|
||||
pub fn push_first(&mut self, num: i32) {
|
||||
if self.que_size == self.capacity() {
|
||||
println!("双向队列已满");
|
||||
return
|
||||
return;
|
||||
}
|
||||
// 队首指针向左移动一位
|
||||
// 通过取余操作实现 front 越过数组头部后回到尾部
|
||||
@ -3046,7 +3045,7 @@ comments: true
|
||||
pub fn push_last(&mut self, num: i32) {
|
||||
if self.que_size == self.capacity() {
|
||||
println!("双向队列已满");
|
||||
return
|
||||
return;
|
||||
}
|
||||
// 计算队尾指针,指向队尾索引 + 1
|
||||
let rear = self.index(self.front as i32 + self.que_size as i32);
|
||||
@ -3073,18 +3072,22 @@ comments: true
|
||||
|
||||
/* 访问队首元素 */
|
||||
fn peek_first(&self) -> i32 {
|
||||
if self.is_empty() { panic!("双向队列为空") };
|
||||
if self.is_empty() {
|
||||
panic!("双向队列为空")
|
||||
};
|
||||
self.nums[self.front]
|
||||
}
|
||||
|
||||
/* 访问队尾元素 */
|
||||
fn peek_last(&self) -> i32 {
|
||||
if self.is_empty() { panic!("双向队列为空") };
|
||||
if self.is_empty() {
|
||||
panic!("双向队列为空")
|
||||
};
|
||||
// 计算尾元素索引
|
||||
let last = self.index(self.front as i32 + self.que_size as i32 - 1);
|
||||
self.nums[last]
|
||||
}
|
||||
|
||||
|
||||
/* 返回数组用于打印 */
|
||||
fn to_array(&self) -> Vec<i32> {
|
||||
// 仅转换有效长度范围内的列表元素
|
||||
|
@ -959,9 +959,9 @@ comments: true
|
||||
/* 基于链表实现的队列 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListQueue<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 队列的长度
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // 头节点 front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // 尾节点 rear
|
||||
que_size: usize, // 队列的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListQueue<T> {
|
||||
@ -969,7 +969,7 @@ comments: true
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1887,10 +1887,10 @@ comments: true
|
||||
```rust title="array_queue.rs"
|
||||
/* 基于环形数组实现的队列 */
|
||||
struct ArrayQueue {
|
||||
nums: Vec<i32>, // 用于存储队列元素的数组
|
||||
front: i32, // 队首指针,指向队首元素
|
||||
que_size: i32, // 队列长度
|
||||
que_capacity: i32, // 队列容量
|
||||
nums: Vec<i32>, // 用于存储队列元素的数组
|
||||
front: i32, // 队首指针,指向队首元素
|
||||
que_size: i32, // 队列长度
|
||||
que_capacity: i32, // 队列容量
|
||||
}
|
||||
|
||||
impl ArrayQueue {
|
||||
|
@ -876,8 +876,8 @@ comments: true
|
||||
/* 基于链表实现的栈 */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListStack<T> {
|
||||
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // 将头节点作为栈顶
|
||||
stk_size: usize, // 栈的长度
|
||||
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // 将头节点作为栈顶
|
||||
stk_size: usize, // 栈的长度
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListStack<T> {
|
||||
@ -1537,7 +1537,9 @@ comments: true
|
||||
impl<T> ArrayStack<T> {
|
||||
/* 初始化栈 */
|
||||
fn new() -> ArrayStack<T> {
|
||||
ArrayStack::<T> { stack: Vec::<T>::new() }
|
||||
ArrayStack::<T> {
|
||||
stack: Vec::<T>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取栈的长度 */
|
||||
@ -1565,7 +1567,9 @@ comments: true
|
||||
|
||||
/* 访问栈顶元素 */
|
||||
fn peek(&self) -> Option<&T> {
|
||||
if self.is_empty() { panic!("栈为空") };
|
||||
if self.is_empty() {
|
||||
panic!("栈为空")
|
||||
};
|
||||
self.stack.last()
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ comments: true
|
||||
|
||||
<p align="center"> 图 7-12 完美二叉树的数组表示 </p>
|
||||
|
||||
**映射公式的角色相当于链表中的指针**。给定数组中的任意一个节点,我们都可以通过映射公式来访问它的左(右)子节点。
|
||||
**映射公式的角色相当于链表中的引用**。给定数组中的任意一个节点,我们都可以通过映射公式来访问它的左(右)子节点。
|
||||
|
||||
## 7.3.2 表示任意二叉树
|
||||
|
||||
|
@ -1819,6 +1819,7 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区
|
||||
}
|
||||
}
|
||||
Self::update_height(Some(node.clone())); // 更新节点高度
|
||||
|
||||
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
|
||||
node = Self::rotate(Some(node)).unwrap();
|
||||
// 返回子树的根节点
|
||||
@ -2343,6 +2344,7 @@ AVL 树的节点插入操作与二叉搜索树在主体上类似。唯一的区
|
||||
node.borrow_mut().val = temp.borrow().val;
|
||||
}
|
||||
Self::update_height(Some(node.clone())); // 更新节点高度
|
||||
|
||||
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
|
||||
node = Self::rotate(Some(node)).unwrap();
|
||||
// 返回子树的根节点
|
||||
|
@ -1291,8 +1291,8 @@ comments: true
|
||||
/* 删除节点 */
|
||||
pub fn remove(&mut self, num: i32) {
|
||||
// 若树为空,直接提前返回
|
||||
if self.root.is_none() {
|
||||
return;
|
||||
if self.root.is_none() {
|
||||
return;
|
||||
}
|
||||
let mut cur = self.root.clone();
|
||||
let mut pre = None;
|
||||
|
@ -233,13 +233,14 @@ comments: true
|
||||
// 初始化一个列表,用于保存遍历序列
|
||||
let mut vec = Vec::new();
|
||||
|
||||
while let Some(node) = que.pop_front() { // 队列出队
|
||||
vec.push(node.borrow().val); // 保存节点值
|
||||
while let Some(node) = que.pop_front() {
|
||||
// 队列出队
|
||||
vec.push(node.borrow().val); // 保存节点值
|
||||
if let Some(left) = node.borrow().left.as_ref() {
|
||||
que.push_back(Rc::clone(left)); // 左子节点入队
|
||||
que.push_back(Rc::clone(left)); // 左子节点入队
|
||||
}
|
||||
if let Some(right) = node.borrow().right.as_ref() {
|
||||
que.push_back(Rc::clone(right)); // 右子节点入队
|
||||
que.push_back(Rc::clone(right)); // 右子节点入队
|
||||
};
|
||||
}
|
||||
vec
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{% block announce %}
|
||||
{% if config.theme.language == 'zh' %}
|
||||
{% set announcements = '纸质书已发布,详情请见<a href="/chapter_paperbook/">纸质书介绍</a>' %}
|
||||
{% set announcements = '纸质书已发布,详情请见<a href="/chapter_paperbook/">这里</a>' %}
|
||||
{% elif config.theme.language == 'en' %}
|
||||
{% set announcements = 'The paper book (Chinese edition) published. Please visit <a href="/chapter_paperbook/">this link</a> for more details.' %}
|
||||
{% endif %}
|
||||
|
Reference in New Issue
Block a user