learning-rust/part7/vector练习题.md

30 KiB
Raw Blame History

Rust Vector 练习题

建议先手动写出每道题的答案,再运行代码验证。

目录


一、概念辨析题

判断以下说法是否正确,并简要说明原因。

题目 1-1

Vec<T> 只能存储相同类型的数据。

题目 1-2

从空 Vec 中 pop() 会引发 panic。

题目 1-3

以下两种创建 Vec 的方式完全等价,没有性能差异:

let v1 = Vec::new();
let v2 = vec![];

题目 1-4

使用索引 v[3] 访问 Vec 元素和使用 v.get(3) 的行为完全一致。

题目 1-5

对 Vec 进行 for 循环遍历会消耗 Vec 的所有权,遍历后 Vec 不再可用。

题目 1-6

当 Vec 的容量满了再 push 新元素时Rust 会自动重新分配一块更大的内存并将原数据拷贝过去。

题目 1-7

Vec::with_capacity(n) 创建的 Vec 的长度也为 n

题目 1-8

可以在遍历 Vec 的同时向其中添加元素。


二、填空题:补充代码

补全下列代码使其能通过编译并达到期望输出。

题目 2-1基本创建和操作

fn main() {
    // 填空:创建一个包含元素 1, 2, 3, 4, 5 的 Vec
    let mut v = ________;

    // 填空:向末尾添加元素 6
    v.________(6);

    // 填空:移除并返回最后一个元素
    let last = v.________();
    println!("移除的元素是: {}", last);

    // 填空:获取当前长度
    println!("当前长度: {}", ________);

    // 填空:获取指定位置元素的值(安全方式)
    match v.________(2) {
        Some(value) => println!("索引 2 的值是: {}", value),
        None => println!("没有该元素"),
    }
}
// 期望输出:
// 移除的元素是: 6
// 当前长度: 5
// 索引 2 的值是: 3

题目 2-2遍历 Vec

fn main() {
    let v = vec![10, 20, 30, 40, 50];

    // 填空:不可变遍历,打印每个元素
    for ________ in ________ {
        print!("{} ", i);
    }
    println!();

    // 填空:可变遍历,每个元素翻倍
    let mut v2 = vec![1, 2, 3, 4, 5];
    for ________ in ________ {
        *i ________ 2;
    }
    println!("翻倍后: {:?}", v2);
}
// 期望输出:
// 10 20 30 40 50
// 翻倍后: [2, 4, 6, 8, 10]

题目 2-3插入和删除

fn main() {
    let mut v = vec![1, 2, 4, 5];

    // 填空:在索引 2 处插入元素 3
    v.________(2, 3);

    // 填空:删除索引 4 处的元素
    v.________(4);
    // 填空:删除索引 0 处的元素
    v.________(0);

    println!("结果: {:?}", v);
}
// 期望输出:结果: [2, 3, 4]

题目 2-4枚举实现不同类型存储

// 填空:定义一个枚举,包含整数、浮点数、文本三种变体
________ Value {
    Integer(i32),
    Float(f64),
    ________(String),
}

fn main() {
    // 填空:使用宏创建一个包含不同变体的 Vec
    let row = ________;
        Value::Integer(3),
        Value::Float(3.14),
        Value::Text(String::from("Hello")),
    ];

    for v in &row {
        match v {
            Value::Integer(n) => print!("Int:{} ", n),
            Value::Float(f) => print!("Float:{} ", f),
            Value::Text(s) => print!("Text:{} ", s),
        }
    }
}
// 期望输出Int:3 Float:3.14 Text:Hello

题目 2-5切片操作

fn main() {
    let v = vec![1, 2, 3, 4, 5, 6, 7, 8];

    // 填空:获取 v[1..4] 的切片
    let slice1 = ________;
    println!("slice1: {:?}", slice1);

    // 填空:获取前 3 个元素的切片
    let slice2 = ________;
    println!("slice2: {:?}", slice2);

    // 填空:获取从索引 5 到末尾的切片
    let slice3 = ________;
    println!("slice3: {:?}", slice3);
}
// 期望输出:
// slice1: [2, 3, 4]
// slice2: [1, 2, 3]
// slice3: [6, 7, 8]

题目 2-6容量管理

fn main() {
    // 填空:预分配容量为 100 的空 Vec
    let mut v: Vec<i32> = Vec::________(100);

    println!("创建后 - 长度: {},容量: {}", v.len(), v.capacity());

    for i in 0..50 {
        v.push(i);
    }
    println!("添加 50 个元素后 - 长度: {},容量: {}", v.len(), v.capacity());

    // 填空:收缩容量以匹配长度(使用合适的方法)
    v.________();
    println!("收缩后 - 长度: {},容量: {}", v.len(), v.capacity());
}

题目 2-7常用方法组合

fn main() {
    let mut v = vec![5, 2, 8, 1, 9, 3];

    // 填空:排序(升序)
    v.________();

    // 填空:检查是否包含某个值
    let has_five = v.________(&5);

    // 填空:反转顺序
    v.________();

    println!("排序并反转后: {:?}", v);
    println!("是否包含 5: {}", has_five);
}
// 期望输出:
// 排序并反转后: [9, 8, 5, 3, 2, 1]
// 是否包含 5: true

题目 2-8extend 和 drain

fn main() {
    let mut v1 = vec![1, 2, 3];
    let v2 = vec![4, 5, 6];

    // 填空:将 v2 的所有元素追加到 v1
    v1.________(&v2);
    println!("追加后: {:?}", v1);

    let mut v = vec![10, 20, 30, 40, 50];
    // 填空:移除索引 1..4 的元素,返回被移除元素的迭代器
    let drained: Vec<_> = v.________(1..4).collect();
    println!("被移除的: {:?}", drained);
    println!("剩余的: {:?}", v);
}
// 期望输出:
// 追加后: [1, 2, 3, 4, 5, 6]
// 被移除的: [20, 30, 40]
// 剩余的: [10, 50]

题目 2-9迭代器适配器

fn main() {
    let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // 填空:过滤出偶数,映射为平方,收集到新 Vec
    let result: Vec<i32> = v.________()
        .________(|x| x % 2 == 0)
        .________(|x| x * x)
        .collect();

    println!("偶数的平方: {:?}", result);

    // 填空:求和
    let sum: i32 = v.iter().________();
    println!("总和: {}", sum);
}
// 期望输出:
// 偶数的平方: [4, 16, 36, 64, 100]
// 总和: 55

三、找出并修复错误

以下每段代码都有编译错误,请指出错误并写出修正后的代码。

题目 3-1

fn main() {
    let mut v = vec![1, 2, 3];
    let first = &v[0];    // 不可变借用
    v.push(4);            // 可变借用
    println!("{}", first); // 使用不可变借用
}

题目 3-2

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    let third = v[10];
    println!("{}", third);
}

题目 3-3

fn main() {
    let mut v = Vec::new();
    v.push("hello");
    v.push(42);
    println!("{:?}", v);
}

题目 3-4

fn main() {
    let mut v = vec![1, 2, 3];
    for i in v.iter_mut() {
        v.push(4);  // 同时修改
    }
    println!("{:?}", v);
}

题目 3-5

fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
    for i in 0..v.len() {
        if v[i] % 2 == 0 {
            v.remove(i);
        }
    }
    println!("{:?}", v);
}

题目 3-6

fn main() {
    let v = vec![String::from("hello")];
    let s = &v[0];   // 借用了 v 中的元素
    drop(v);         // 显式释放 v
    println!("{}", s); // 使用了 s
}

题目 3-7

fn main() {
    let v = vec![1, 2, 3];
    // 想要获得 [1, 4, 9]
    let squared: Vec<i32> = v.iter().map(|x| x * x);
    println!("{:?}", squared);
}

题目 3-8

fn main() {
    let mut v: Vec<u32> = Vec::new();
    v.push(1);
    let popped = v.pop();
    println!("{:?}", popped);   // 期望打印 1
    let popped2 = v.pop();      // Vec 已空
    println!("{}", popped2);    // 这行会怎样?
}

题目 3-9

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    for item in v {
        println!("{}", item);
    }
    println!("v 还有 {} 个元素", v.len()); // 再次使用 v
}

题目 3-10

fn main() {
    let mut v = vec![1, 2, 3];
    let slice = &mut v[0..2];  // 可变借用切片
    v.push(4);                 // 尝试修改原 Vec
    slice[0] = 10;
    println!("{:?}", v);
}

四、编程题

请在 main.rs 中编写以下程序,并实际运行验证。

题目 4-1成绩统计分析器

编写一个程序,完成以下功能:

  1. 创建一个包含 10 个学生成绩的 Vec<i32>(成绩范围 0~100
  2. 计算并打印:最高分、最低分、平均分
  3. 找出所有不及格(< 60的成绩存入新 Vec 并打印
  4. 判断是否有满分100 分),打印结果
  5. 将所有成绩从高到低排序后打印

期望输出示例(成绩不同则输出不同):

成绩列表: [85, 42, 96, 78, 55, 88, 91, 63, 100, 74]
最高分: 100
最低分: 42
平均分: 77.2
不及格成绩: [42, 55]
有满分: 是
排序后(高到低): [100, 96, 91, 88, 85, 78, 74, 63, 55, 42]

题目 4-2斐波那契数列生成器

编写一个函数 fibonacci(n: usize) -> Vec<u64>,接收项数 n,返回一个包含前 n 项斐波那契数列的 Vec。

  • n = 0 时返回空 Vec
  • n = 1 时返回 [1]
  • n = 2 时返回 [1, 1]
  • n >= 3 时正常生成

main 中调用并打印前 20 项。

期望输出(前 20 项):

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

题目 4-3购物车模拟

编写一个模拟购物车的程序:

  1. 定义一个 Item 结构体,包含 name: Stringprice: f64
  2. 创建一个购物车 Vec<Item>,提供以下功能:
    • add_item(cart: &mut Vec<Item>, name: &str, price: f64) — 添加商品
    • remove_item(cart: &mut Vec<Item>, name: &str) — 按名称移除商品(只移除第一个匹配项)
    • total_price(cart: &Vec<Item>) -> f64 — 计算总价
    • list_items(cart: &Vec<Item>) — 列出所有商品
  3. main 中演示完整流程:添加 5 个商品,列出清单,计算总价,移除一个商品,再次列出清单和计算总价

期望输出示例:

购物车清单:
  - 苹果: ¥3.50
  - 香蕉: ¥2.00
  - 牛奶: ¥6.80
  - 面包: ¥5.00
  - 鸡蛋: ¥12.00
当前总价: ¥29.30

移除"香蕉"后:
购物车清单:
  - 苹果: ¥3.50
  - 牛奶: ¥6.80
  - 面包: ¥5.00
  - 鸡蛋: ¥12.00
当前总价: ¥27.30

题目 4-4单词统计器

编写一个程序,统计一段文本中每个单词出现的次数。

  1. 定义输入文本:"the quick brown fox jumps over the lazy dog the fox was quick"
  2. 将文本按空格分割成单词,存入 Vec<&str>
  3. 使用一个辅助的 Vec或使用 HashMap,但核心流程用 Vec 实现)统计每个不重复单词及其出现次数
  4. 打印所有不重复单词及其出现次数(按单词字母顺序排序)

提示:

  • 可以用 Vec<(String, i32)> 存储"单词-次数"对
  • 遍历单词 Vec如果在统计 Vec 中找到该单词则计数+1否则追加新纪录

期望输出:

单词统计结果:
  brown: 1
  dog: 1
  fox: 2
  jumps: 1
  lazy: 1
  over: 1
  quick: 2
  the: 3
  was: 1

题目 4-5矩阵转置

使用 Vec<Vec<i32>> 表示一个二维矩阵,编写一个函数 transpose(matrix: &Vec<Vec<i32>>) -> Vec<Vec<i32>> 实现矩阵转置。

  • 输入:一个 m×n 的矩阵
  • 输出:一个 n×m 的转置矩阵
  • 使用迭代器方法实现,避免使用显式的 C 风格循环

main 中测试:

原始矩阵:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

转置后:
[1, 4, 7]
[2, 5, 8]
[3, 6, 9]

题目 4-6简易栈Stack实现

只使用 Vec 实现一个 Stack<T> 结构体,封装栈操作:

  • fn new() -> Self
  • fn push(&mut self, item: T)
  • fn pop(&mut self) -> Option<T>
  • fn peek(&self) -> Option<&T>
  • fn is_empty(&self) -> bool
  • fn len(&self) -> usize

main 中测试括号匹配功能:编写函数 is_balanced(s: &str) -> bool,利用你实现的 Stack 判断字符串中的 ()[]{} 是否正确匹配。

测试用例:

测试用例:
"()"         -> true
"()[]{}"     -> true
"(]"         -> false
"([)]"       -> false
"{[]}"       -> true
"((("        -> false
""           -> true

五、综合思考题

题目 5-1

阅读以下代码:

fn main() {
    let mut v = vec![String::from("Rust"), String::from("is"), String::from("awesome")];

    let first = &v[0];      // ①
    v.push(String::from("!")); // ②
    println!("{}", first);  // ③
}
  1. 这段代码能编译吗?为什么?
  2. 如果改成 let first = &v[0].clone();,会怎样?为什么?
  3. v[0]v.get(0) 的区别在哪里?分别适用于什么场景?
  4. 如果 v 的类型是 Vec<i32>,问题 1 的代码是否能编译?为什么?

题目 5-2

分析以下两种删除 Vec 中所有偶数元素的方式:

方式 A

fn remove_evens_a(v: &mut Vec<i32>) {
    let mut i = 0;
    while i < v.len() {
        if v[i] % 2 == 0 {
            v.remove(i);
        } else {
            i += 1;
        }
    }
}

方式 B

fn remove_evens_b(v: &mut Vec<i32>) {
    v.retain(|x| x % 2 != 0);
}
  1. 两种方式的时间复杂度分别是多少?为什么?
  2. 方式 A 中的 i += 1 放在 else 分支中,如果去掉这个条件(每次循环都 i += 1)会发生什么?
  3. retain 方法的底层原理是什么?它比手动 remove 高效在哪里?

题目 5-3

fn first_n_even(n: usize) -> Vec<i32> {
    let mut result = Vec::new();
    let mut num = 2;
    while result.len() < n {
        if num % 2 == 0 {
            result.push(num);
        }
        num += 1;
    }
    result
}
  1. 上述代码有没有更好的 Rust 风格写法?请使用迭代器重写。
  2. 如果 n 非常大(比如 10000000从性能角度应该如何优化提示Vec::with_capacity 和迭代器)

题目 5-4

阅读以下代码,回答:

fn append_vector(v: &mut Vec<i32>, other: &mut Vec<i32>) {
    v.append(other);
}

fn main() {
    let mut v1 = vec![1, 2, 3];
    let mut v2 = vec![4, 5, 6];

    append_vector(&mut v1, &mut v2);

    println!("v1: {:?}", v1);
    println!("v2: {:?}", v2);
}
  1. v2append 调用后会变成什么?为什么?
  2. appendextend 有什么区别?各适用于什么场景?
  3. 如果希望 v2 在追加后保持不变(即"复制追加"),应该使用什么方法?

题目 5-5

假设你正在实现一个游戏中的物品栏系统:

#[derive(Debug)]
struct Inventory {
    items: Vec<Item>,
    capacity: usize,
}

#[derive(Debug, Clone)]
struct Item {
    name: String,
    count: u32,
}

impl Inventory {
    fn new(capacity: usize) -> Self {
        Inventory {
            items: Vec::new(),
            capacity,
        }
    }

    fn add_item(&mut self, item: Item) -> Result<(), String> {
        // 待实现
        todo!()
    }

    fn remove_item(&mut self, name: &str, count: u32) -> Result<(), String> {
        // 待实现
        todo!()
    }
}
  1. 实现 add_item:如果物品已存在则叠加数量;如果背包已满则返回错误信息。注意借用检查器的约束。
  2. 实现 remove_item:如果数量足够则减少,如果降至 0 则移除整个条目;如果物品不存在或数量不足则返回错误信息。
  3. 这个设计中有一个潜在的问题:当背包容量已满但要添加的是已有物品时,叠加数量并不需要新增槽位。如何修改 add_item 的逻辑来正确处理这种情况?

参考答案

请独立完成再查看答案。

点击展开答案

一、概念辨析题

1-1 正确。Vec<T> 是泛型容器,所有元素必须是相同类型 T。如果要存储不同类型,需要使用枚举(如 Vec<MyEnum>)或 trait 对象(Vec<Box<dyn MyTrait>>)。

1-2 错误。空 Vec 调用 pop() 不会 panic而是返回 Nonepop 的返回类型是 Option<T>,在空 Vec 上返回 None

1-3 错误。Vec::new() 创建一个容量为 0 的 Vec不分配堆内存vec![] 同样不分配。两者在这种情况下等价。但如果使用 vec![1, 2, 3] 和手动 newpush 三次,vec![] 可能预先分配合适的容量,性能略好。

1-4 错误。v[3] 索引越界时会 panic(程序崩溃),而 v.get(3) 返回 Option<&T>,越界时返回 None,不会崩溃。get 是安全的访问方式。

1-5 错误。for item in v 会消耗 Vec 的所有权,但 for item in &v 仅不可变借用,for item in &mut v 仅可变借用。只有转移所有权的遍历才会使 Vec 之后不可用。

1-6 正确。当 len == capacity 时再 pushVec 会自动分配一块更大的内存(通常是当前容量的两倍),将旧元素移动/拷贝到新内存,然后释放旧内存。

1-7 错误。Vec::with_capacity(n) 创建的 Vec 长度为 0容量为 n。需要通过 push 等方法添加元素后长度才会增加。

1-8 错误。在遍历 Vec 时不能同时修改 Vec 本身(添加或删除元素),这违反了借用规则。遍历时持有的是对 Vec 或其元素的借用,而修改需要可变借用,两者不能共存。

二、填空题

2-1

let mut v = vec![1, 2, 3, 4, 5];
v.push(6);
let last = v.pop().unwrap();
println!("当前长度: {}", v.len());
match v.get(2) {

2-2

for i in &v {
    print!("{} ", i);
}
// 可变遍历
for i in &mut v2 {
    *i *= 2;
}

2-3

v.insert(2, 3);
v.remove(4);
v.remove(0);

2-4

enum Value {
    Integer(i32),
    Float(f64),
    Text(String),
}
let row = vec![
    Value::Integer(3),
    Value::Float(3.14),
    Value::Text(String::from("Hello")),
];

2-5

let slice1 = &v[1..4];
let slice2 = &v[..3];
let slice3 = &v[5..];

2-6

let mut v: Vec<i32> = Vec::with_capacity(100);
// ... 添加元素后
v.shrink_to_fit();

2-7

v.sort();
let has_five = v.contains(&5);
v.reverse();

2-8

v1.extend(&v2);
let drained: Vec<_> = v.drain(1..4).collect();

2-9

let result: Vec<i32> = v.iter()
    .filter(|x| *x % 2 == 0)
    .map(|x| x * x)
    .collect();
let sum: i32 = v.iter().sum();

三、修复错误

3-1:不可变借用和可变借用冲突。firstv 的不可变引用,而 push 需要 v 的可变引用。修复:调整顺序,先使用 firstpush,或在 push 前不保留引用。

let mut v = vec![1, 2, 3];
let first = v[0];      // 直接拷贝值i32 实现了 Copy
v.push(4);
println!("{}", first);

对于非 Copy 类型,可以:

let first = v[0].clone();  // 克隆值
v.push(4);
println!("{}", first);

3-2索引越界Vec 只有 5 个元素(索引 0~4访问 v[10] 会 panic。修复

let third = v.get(10);  // 返回 None不会 panic
match third {
    Some(val) => println!("{}", val),
    None => println!("索引越界"),
}

3-3Vec::new() 无法推断类型,需要显式标注。且同一个 Vec 不能混装 &stri32。修复:使用枚举:

enum Value<'a> {
    Text(&'a str),
    Number(i32),
}
let mut v: Vec<Value> = Vec::new();
v.push(Value::Text("hello"));
v.push(Value::Number(42));

3-4:在遍历 Vec 的同时修改 Vec 的大小,违反借用规则。修复:先收集要添加的元素,再 extend

let mut v = vec![1, 2, 3];
v.extend(vec![4, 4, 4]);
println!("{:?}", v);

3-5:一边按索引遍历一边删除元素会导致跳过元素(因为删除后后续元素前移)。修复:使用 retain 或从后往前遍历:

let mut v = vec![1, 2, 3, 4, 5];
v.retain(|x| x % 2 != 0);
println!("{:?}", v); // [1, 3, 5]

3-6s 借用了 v 中的元素,drop(v) 释放 vs 变成悬垂引用。修复:先 clone 再释放:

let v = vec![String::from("hello")];
let s = v[0].clone();  // 获取独立的所有权
drop(v);
println!("{}", s);

3-7map 返回迭代器,需要 .collect() 消费才能得到 Vec。修复

let squared: Vec<i32> = v.iter().map(|x| x * x).collect();

3-8pop 返回 Option<u32>,不能直接用 println!("{}", popped2) 打印(类型不匹配,且值为 None)。修复:

println!("{:?}", popped);   // Some(1)
println!("{:?}", popped2);  // None

3-9for item in v 消耗了 v 的所有权,之后不能再用 v。修复:使用引用遍历:

for item in &v {
    println!("{}", item);
}
println!("v 还有 {} 个元素", v.len());

3-10slicev 的可变借用,push 需要另一个可变借用,冲突。修复:先操作切片,释放后再 push

let mut v = vec![1, 2, 3];
{
    let slice = &mut v[0..2];
    slice[0] = 10;
}  // slice 的借用在此结束
v.push(4);
println!("{:?}", v);  // [10, 2, 3, 4]

四、编程题

4-1 成绩统计分析器:

fn main() {
    let scores = vec![85, 42, 96, 78, 55, 88, 91, 63, 100, 74];
    println!("成绩列表: {:?}", scores);

    let max = scores.iter().max().unwrap();
    let min = scores.iter().min().unwrap();
    let avg = scores.iter().sum::<i32>() as f64 / scores.len() as f64;

    println!("最高分: {}", max);
    println!("最低分: {}", min);
    println!("平均分: {:.1}", avg);

    let failing: Vec<i32> = scores.iter().filter(|&&s| s < 60).copied().collect();
    println!("不及格成绩: {:?}", failing);

    let has_perfect = scores.contains(&100);
    println!("有满分: {}", if has_perfect { "是" } else { "否" });

    let mut sorted = scores.clone();
    sorted.sort_by(|a, b| b.cmp(a));
    println!("排序后(高到低): {:?}", sorted);
}

4-2 斐波那契数列生成器:

fn fibonacci(n: usize) -> Vec<u64> {
    match n {
        0 => vec![],
        1 => vec![1],
        _ => {
            let mut fib = vec![1, 1];
            for _ in 2..n {
                let next = fib[fib.len() - 1] + fib[fib.len() - 2];
                fib.push(next);
            }
            fib
        }
    }
}

fn main() {
    let fib = fibonacci(20);
    println!("{:?}", fib);
}

4-3 购物车模拟:

struct Item {
    name: String,
    price: f64,
}

fn add_item(cart: &mut Vec<Item>, name: &str, price: f64) {
    cart.push(Item {
        name: name.to_string(),
        price,
    });
}

fn remove_item(cart: &mut Vec<Item>, name: &str) {
    if let Some(pos) = cart.iter().position(|item| item.name == name) {
        cart.remove(pos);
    }
}

fn total_price(cart: &Vec<Item>) -> f64 {
    cart.iter().map(|item| item.price).sum()
}

fn list_items(cart: &Vec<Item>) {
    for item in cart {
        println!("  - {}: ¥{:.2}", item.name, item.price);
    }
}

fn main() {
    let mut cart = Vec::new();

    add_item(&mut cart, "苹果", 3.50);
    add_item(&mut cart, "香蕉", 2.00);
    add_item(&mut cart, "牛奶", 6.80);
    add_item(&mut cart, "面包", 5.00);
    add_item(&mut cart, "鸡蛋", 12.00);

    println!("购物车清单:");
    list_items(&cart);
    println!("当前总价: ¥{:.2}", total_price(&cart));

    remove_item(&mut cart, "香蕉");

    println!("\n移除\"香蕉\"后:");
    println!("购物车清单:");
    list_items(&cart);
    println!("当前总价: ¥{:.2}", total_price(&cart));
}

4-4 单词统计器:

fn main() {
    let text = "the quick brown fox jumps over the lazy dog the fox was quick";
    let words: Vec<&str> = text.split_whitespace().collect();

    let mut stats: Vec<(String, i32)> = Vec::new();

    for word in &words {
        if let Some(pos) = stats.iter().position(|(w, _)| w == word) {
            stats[pos].1 += 1;
        } else {
            stats.push((word.to_string(), 1));
        }
    }

    stats.sort_by(|a, b| a.0.cmp(&b.0));

    println!("单词统计结果:");
    for (word, count) in &stats {
        println!("  {}: {}", word, count);
    }
}

4-5 矩阵转置:

fn transpose(matrix: &Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    if matrix.is_empty() {
        return vec![];
    }

    let rows = matrix.len();
    let cols = matrix[0].len();

    (0..cols)
        .map(|col| {
            (0..rows)
                .map(|row| matrix[row][col])
                .collect()
        })
        .collect()
}

fn main() {
    let matrix = vec![
        vec![1, 2, 3],
        vec![4, 5, 6],
        vec![7, 8, 9],
    ];

    println!("原始矩阵:");
    for row in &matrix {
        println!("{:?}", row);
    }

    let transposed = transpose(&matrix);

    println!("\n转置后:");
    for row in &transposed {
        println!("{:?}", row);
    }
}

4-6 简易栈实现:

struct Stack<T> {
    data: Vec<T>,
}

impl<T> Stack<T> {
    fn new() -> Self {
        Stack { data: Vec::new() }
    }

    fn push(&mut self, item: T) {
        self.data.push(item);
    }

    fn pop(&mut self) -> Option<T> {
        self.data.pop()
    }

    fn peek(&self) -> Option<&T> {
        self.data.last()
    }

    fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    fn len(&self) -> usize {
        self.data.len()
    }
}

fn is_balanced(s: &str) -> bool {
    let mut stack = Stack::new();

    for ch in s.chars() {
        match ch {
            '(' | '[' | '{' => stack.push(ch),
            ')' => {
                if stack.pop() != Some('(') {
                    return false;
                }
            }
            ']' => {
                if stack.pop() != Some('[') {
                    return false;
                }
            }
            '}' => {
                if stack.pop() != Some('{') {
                    return false;
                }
            }
            _ => {}
        }
    }

    stack.is_empty()
}

fn main() {
    let tests = [
        ("()", true),
        ("()[]{}", true),
        ("(]", false),
        ("([)]", false),
        ("{[]}", true),
        ("(((", false),
        ("", true),
    ];

    println!("括号匹配测试:");
    for (input, expected) in tests {
        let result = is_balanced(input);
        let status = if result == expected { "✓" } else { "✗" };
        println!("  {} \"{}\" -> {} (期望: {})", status, input, result, expected);
    }
}

五、综合思考题

5-1

  1. 不能编译。firstv[0] 的不可变引用,而 v.push() 需要可变引用。即便 push 触发的内存重分配不影响索引 0 的元素(逻辑上 String 的数据在堆上),但 Rust 的借用规则在编译期不能精确判断这一点,因此直接拒绝在持有不可变引用时进行可变操作。

  2. v[0].clone() 会创建一个新的 Stringfirst 拥有这个新 String 的所有权,不再依赖 v。因此 v.push() 不受影响,可以编译。

  3. v[0] 返回 &T,越界时 panicv.get(0) 返回 Option<&T>,越界时返回 Nonev[0] 适用于确定索引有效的场景,v.get(0) 适用于索引可能越界的场景。

  4. 可以编译。因为 i32 实现了 Copy traitlet first = &v[0] 在后续使用中,由于 first&i32println! 打印时需要解引用为 i32 值。但关键是将 let first = &v[0] 改成 let first = v[0] 后,firsti32Copyv 无关,更安全。使用 &v[0] 仍然会有借用冲突。

5-2

  1. 方式 A 的时间复杂度是 O(n²),因为每次 remove(i) 都需要将后续元素向前移动一位。方式 B 的 retain 是 O(n),内部使用一次遍历 + 一次批量移动实现。

  2. 如果每次都 i += 1,删除偶数元素后索引不会重新检查当前位置(因为后续元素前移了),会跳过紧随其后的元素。例如 [2, 2, 1],删除索引 0 的元素 2 后,数组变为 [2, 1]i 变成 1第二个 2 被跳过。

  3. retain 的底层原理类似于"双指针":慢指针 j 指向下一个要保留的位置,快指针 i 遍历所有元素。当元素满足保留条件时,移动到 j 位置并使 j++;否则跳过。遍历完成后截断到 j。避免了每次删除都移动后续元素的 O(n²) 开销。

5-3

  1. 迭代器重写:
fn first_n_even(n: usize) -> Vec<i32> {
    (1..).filter(|x| x % 2 == 0).take(n).collect()
}
  1. 优化:
fn first_n_even_optimized(n: usize) -> Vec<i32> {
    let mut result = Vec::with_capacity(n);  // 预分配
    (1..).filter(|x| x % 2 == 0).take(n).for_each(|x| result.push(x));
    result
}
// 或者更简单的:从 2 开始每次加 2
fn first_n_even_optimized(n: usize) -> Vec<i32> {
    (2..).step_by(2).take(n).collect()
}

使用 Vec::with_capacity(n) 预分配可以避免多次重分配的开销。

5-4

  1. v2 变成空 Vec []。因为 append 方法将所有元素移动到目标 Vec并清空源 Vec。这是 O(1) 时间,只交换了指针。

  2. 区别:

    • append:移动所有元素,源 Vec 被清空。适用于需要转移所有权的场景。
    • extend:从迭代器克隆/复制元素到目标 Vec源数据保持不变。适用于需要保留源的场景。
    • 性能上 append 只需交换内部指针,是 O(1)extend 需要逐个复制,是 O(n)。
  3. 使用 extendclone

v1.extend_from_slice(&v2);  // v2 保持不变
// 或
v1.extend(v2.iter().cloned()); // 迭代器方式

5-5

fn add_item(&mut self, item: Item) -> Result<(), String> {
    // 先检查是否有同名物品
    if let Some(existing) = self.items.iter_mut().find(|i| i.name == item.name) {
        existing.count += item.count;
        return Ok(());
    }

    // 新增物品,检查容量
    if self.items.len() >= self.capacity {
        return Err(format!("背包已满({}/{}", self.items.len(), self.capacity));
    }

    self.items.push(item);
    Ok(())
}

fn remove_item(&mut self, name: &str, count: u32) -> Result<(), String> {
    let pos = self
        .items
        .iter()
        .position(|i| i.name == name)
        .ok_or(format!("物品「{}」不存在", name))?;

    if self.items[pos].count < count {
        return Err(format!(
            "物品「{}」数量不足(需要 {},当前 {}",
            name, count, self.items[pos].count
        ));
    }

    self.items[pos].count -= count;
    if self.items[pos].count == 0 {
        self.items.remove(pos);
    }

    Ok(())
}

关键点是先检查是否可以叠加已有物品(不需要新槽位),再检查容量限制。这样当物品已存在时,即使背包"已满"也能正确叠加数量。