Vector是Rust标准库提供的动态数组。一个vector对象里可以保存若干相同类型的数据。
创建一个动态数组需要用Vec<T>
的形式指定元素类型:
let v: Vec<i32> = Vec::new();
这样就能得到一个空数组。如果有初始值,我们可以用vec!这个宏,通过编译器类型推导,可以省去元素类型:
let mut a = vec![1, 3, 4];
vec!
还有一种语法是重复一个值若干次:
let mut a = vec![1; 10]; // 10个1
现在我们有了数组,如果要向数组尾部添加数据,可以用push
方法:
a.push(4);
对应的,pop
方法可以移除并且返回最后一个元素:
let last = a.pop();
跟String
类似,len
方法可以得到目前元素的个数。
let n = a.len();
使用方括号可以用下标访问数组内的元素(第一个元素是[0]):
let second = a[1];
注意如果在运行中下标越界会导致程序崩溃(panic):
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 4', vec.rs:4:34
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
除了常见的for i = 0..a.len()
使用下标访问,也可以使用iter()
以迭代器模式遍历数组中的所有元素:
for i in a.iter() {
...
}
Vec也可以切片。在定义函数参数时,一般建议传递切片,而不是原对象。数组切片&[T]
是一个对Vec的只读的借用,所以没有Vec提供的动态管理的接口,比如push
,pop
等:
fn show_vec(v: &[i32]) {
for i in v.iter() {
println!("{}", i);
}
}
pub fn main() {
let mut a = vec![1, 2, 3];
show_vec(&a);
}
Vec的内存管理
Vec内部数据非常简单,它实际上由3部分组成:
- 一段动态分配的内存
- 这段内存的容量
- 当前元素个数
Vec的内存管理逻辑也非常简单。
一个新的Vec默认是空的,既没有动态分配内存(也就是容量为0),也没有元素。当调用push
时,Vec对象会检查当前容量是否能容纳一个新的元素,如果不能,所有的数据会被挪到一个新分配的更大的内存里。
也就是说Vec里元素占用的空间必须小于等于内存容量。为了避免性能上和空间上的不确定性,Vec会自动扩容,但是即使元素被删除,也不会自动收缩。
如果是一个局部短期使用的Vec,它在作用域结束的时候,会被编译器自动销毁,释放内部的空间。这种情况一般不需要担心内存管理。
如果是一个长期存在的比较大的Vec(比如作为全局缓存),那就需要注意在必要的时候缩小空间,以免造成内存压力。Vec提供了非常完整的接口允许使用者安全的管理Vec底层使用的内存。通过调用这些方法,一方面可以做到在连续插入时避免频繁的空间重新分配,同时在删除数据后也能及时释放不需要的空间。
pub fn main() {
let mut a = vec![1, 2, 3];
println!("capacity of a is {}", a.capacity());
a.reserve(100);
println!("capacity of a is {}", a.capacity());
a.shrink_to_fit();
println!("capacity of a is {}", a.capacity());
a.truncate(2);
println!("capacity of a is {}", a.capacity());
a.shrink_to_fit();
println!("capacity of a is {}", a.capacity());
}
执行输出:
capacity of a is 3
capacity of a is 103
capacity of a is 3
capacity of a is 3
capacity of a is 2
Vec的高阶函数
Vec提供了一些方便的元素操作函数。这些方法接受一个函数作为参数,然后用这个函数作为标准去操作数组里的元素。
一个典型的例子就是自定义排序:
pub fn main() {
let mut a = vec![(1, 3), (2, 1), (3, 5)];
a.sort_by(|&a, &b| a.1.cmp(&b.1));
println!("a is {:?}", a);
}
这里我们用一个匿名函数|&a, &b| a.1.cmp(&b.1)
作为sort_by
的参数,比较的标准是每个元组的第二个元素(.1
)。
另一个例子,使用retain
方法删除3的倍数:
pub fn main() {
let mut a = vec![1, 2, 3, 4, 5];
a.retain(|&x| x % 3 != 0);
println!("a is {:?}", a);
}
// 输出:
// a is [1, 2, 4, 5]
类似的高阶函数还有dedup_by
,resize_with
,binary_search_by
,is_sorted_by
等。
Vec的完整接口可以在本节最后的链接中找到。
最后我们用LeetCode 56来练习一下Vec的使用吧。
题目:
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: intervals = [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
注意:输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。
提示:
intervals[i][0] <= intervals[i][1]
答案:
impl Solution {
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut ans: Vec<Vec<i32>> = Vec::new();
if intervals.len() == 0 {
return ans;
}
let mut all = Vec::new();
for x in intervals.iter() {
all.push((x[0], 1));
all.push((x[1], -1));
}
all.sort_by(|x, y|
if x.0 == y.0 {
let x1 = -x.1;
let y1 = -y.1;
x1.cmp(&y1)
} else {
x.0.cmp(&y.0)
});
let mut c = 0;
let mut begin = all[0].0;
for x in all {
if c == 0 {
begin = x.0;
}
c += x.1;
if c == 0 {
ans.push(vec![begin, x.0]);
}
}
ans
}
}
思路:
想象你是一个公交车售票员,每一个区间可以看作一个乘客的上车和下车的车站编号,那么题目所求的就是所有车上有乘客的区间。为了得出这些区间,你可以拿一个可以加也可以减的计数器。每上一个乘客,计数加1,每下一个乘客,计数减1。当计数从0加到1时,车上开始有乘客;当计数从1减到0的时候,车上就没有乘客了。这两个时刻构成的区间,就是所求的答案。
所以我们把上下车的时刻排序,然后按时间先后数人数。注意同一时刻上下车的话总是先数上车的人,这样可以达到合并相邻区间的效果。
Vec类型的参考文档: https://doc.rust-lang.org/std/vec/struct.Vec.html
关注红小豆,一起学习Rust开发。欢迎点赞,转发,收藏!