【边刷Leetcode边学Rust】56. 合并区间
今天,我们通过56. 合并区间这道题来学习Rust中"元组型结构体(tuple-like structs) "和"向量(vector)的切片(slice)"这两个概念(语法项)。
首先来看题目给出的函数的定义(签名),
rust
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>>
这里是用Vec<i32>
来表示区间的。这种表示方法显然不够直观,因为Vec<i32>
可以包含任意个元素,而一个区间必须由[start, end]
两个元素构成。
元组型结构体(tuple-like structs)
我们可以定义如下的元组型结构体 明确表示merge
函数处理的对象是一系列区间 (Vec<Interval>
)。
rust
struct Interval(i32, i32);
// pub fn merge(intervals: Vec<Interval>) -> Vec<Interval>
构造一个区间非常简单,还可以通过形如.0
、.1
的语法来访问区间的起点和终点。
rust
struct Interval(i32, i32);
fn main() {
let a = Interval(1, 2);
println!("[{}, {}]", a.0, a.1);
}
元组型结构体与类型别名
表达式
Interval(1, 2)
看起来像函数调用,实际上也确实隐式定义了一个函数:
rust
fn Interval(elem0: i32, elem1: i32) -> Interval { ... }
于是这就带来了一个小问题:
rust
struct Foo(i32);
type Bar = Foo;
fn main() {
Bar(1);
}
// |
// 5 | Bar(1);
// | ^^^
// |
// = note: can't use a type alias as a constructor
无法直接使用元组型结构体的类型别名(type alias)构造值,除非再定义一个
Bar()
函数
rust
fn Bar(a: i32) -> i32 { a }
合并算法(递归)
了解了元组型结构体的使用方法后,我们先将参数Vec<Vec<i32>>
中的区间按起点排序(因为在已排序的区间列表中,可以合并的区间一定是连续的),再转换成Vec<Interval>
。
rust
struct Interval(i32, i32);
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
// sort intervals by start
let mut intervals = intervals;
intervals.sort_by(|a, b| a[0].cmp(&b[0]));
// convert `Vec<Vec<i32>>` to `Vec<Interval>`
let mut sorted_intervals = Vec::<Interval>::with_capacity(intervals.len());
for interval in intervals {
sorted_intervals.push(Interval(interval[0], interval[1]));
}
// ...
}
如图所示,经过排序后,标记为蓝色、黄色和绿色的区间分别可以合并成一个大区间。
如果用递归的思路,合并过程可以分为几步:
-
若整个待合并的区间列表为空,直接返回空列表;否则
-
将整个待合并的区间列表分割为两部分:可合并的部分 + 剩余部分
-
合并前者,将合并成的大区间放入结果中
-
对后者的剩余部分(视作新的整个待合并的区间列表)执行步骤0~2
用伪代码表示就是
rust
fn merge(sorted_intervals: Vec<Intervals>) -> Vec<Intervals> {
if sorted_intervals.len() == 0 {
return vec![];
}
let mut ans = vec![];
mergeable, remaining = split(sorted_intervals)
result.push(mergeable);
// merge the rest intervals recursively
result.append(merge(remaining));
return result;
}
向量(vector)的切片(slice)
在分割区间列表sorted_intervals: Vec<Intervals>
时,显然无须生成区间列表的副本,只要记录分割位置即可。
在Rust中,类型&[T]
称为类型T
的共享切片,是对一系列元素的引用,这些元素来自于数组或向量。或者说,切片是数组或向量中的一个区域。例如:
rust
#[derive(Debug)]
struct Interval(i32, i32);
fn main() {
let interval_vec = vec![
Interval(1, 2),
Interval(4, 9),
Interval(8, 10),
Interval(0, 0),
];
print_interval_slice(&interval_vec); // [Interval(1, 2), Interval(4, 9), Interval(8, 10), Interval(0, 0)]
print_interval_slice(&interval_vec[0..2]); // [Interval(1, 2), Interval(4, 9)]
print_interval_slice(&interval_vec[2..]); // [Interval(8, 10), Interval(0, 0)]
}
fn print_interval_slice(interval_slice: &[Interval]) { // 以切片为参数
println!("{:?}", interval_slice);
}
对于本题,合并操作只需要处理Vec<Interval>
的切片。所以我们定义了_merge()
函数:
rust
fn _merge(sorted_intervals: &[Interval]) -> Vec<Interval> {
对整个sorted_intervals
的引用&sorted_intervals
是一个切片,也可以通过加上[..]
来强调这一点,即&sorted_intervals[..]
。
当找到了分割位置i
,以i
为起点的部分区间列表&sorted_intervals[i..]
又是切片。所以只需要递归调用_merge()
函数即可合并所有能够合并的区间。
rust
fn _merge(sorted_intervals: &[Interval]) -> Vec<Interval> {
if sorted_intervals.len() == 0 {
// 递归结束条件
return vec![];
}
let mut i = 0;
let mut max_end = sorted_intervals[0].1;
let mut mergeable = Interval(sorted_intervals[0].0, max_end);
let mut result = vec![];
// 寻找分割位置`i`
while i < sorted_intervals.len() {
if sorted_intervals[i].0 > max_end {
break;
}
// update `max_end`
max_end = max_end.max(sorted_intervals[i].1);
i += 1;
}
println!{"{:?}\t i = {}", mergeable, i};
// found first mergeable interval
mergeable.1 = max_end;
result.push(mergeable);
// merge the rest intervals recursively
result.append(&mut Self::_merge(&sorted_intervals[i..]));
return result;
}
完整的代码如下所示。
rust
pub struct Solution;
#[derive(Debug, PartialEq, Eq)]
struct Interval(i32, i32);
impl Solution {
///
/// ```
/// use rust_leetcode::lc56::Solution;
/// assert_eq!(vec![vec![1,6],vec![8,10],vec![15,18]], Solution::merge(vec![vec![1,3],vec![2,6],vec![8,10],vec![15,18]]));
/// // assert_eq!(vec![vec![1,5]], Solution::merge(vec![vec![1,4],vec![4,5]]));
/// // assert_eq!(vec![vec![0,5]], Solution::merge(vec![vec![4,5],vec![1,4], vec![0,1]]));
/// ```
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
// sort intervals by start
let mut intervals = intervals;
intervals.sort_by(|a, b| a[0].cmp(&b[0]));
let mut sorted_intervals = Vec::<Interval>::with_capacity(intervals.len());
for interval in intervals {
sorted_intervals.push(Interval(interval[0], interval[1]));
}
let result = Self::_merge(&sorted_intervals[..]);
let mut ans = Vec::<Vec<i32>>::with_capacity(result.len());
for interval in result {
ans.push(vec![interval.0, interval.1]);
}
return ans;
}
fn _merge(sorted_intervals: &[Interval]) -> Vec<Interval> {
if sorted_intervals.len() == 0 {
return vec![];
}
let mut i = 0;
let mut max_end = sorted_intervals[0].1;
let mut mergeable = Interval(sorted_intervals[0].0, max_end);
let mut result = vec![];
while i < sorted_intervals.len() {
if sorted_intervals[i].0 > max_end {
break;
}
// update `max_end`
max_end = max_end.max(sorted_intervals[i].1);
i += 1;
}
println!{"{:?}\t i = {}", mergeable, i};
// found first mergeable interval
mergeable.1 = max_end;
result.push(mergeable);
// merge the rest intervals recursively
result.append(&mut Self::_merge(&sorted_intervals[i..]));
return result;
}
}