new vector 大数组_一起学Rust编程「9」:动态数组Vectors

bdbdb262851cb9597fe5205fd99e9bab.png

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提供的动态管理的接口,比如pushpop等:

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_byresize_withbinary_search_byis_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开发。欢迎点赞,转发,收藏!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值