二、基础数据结构
1、线性数据结构
数组、栈、队列、双端队列、链表这类数据结构都是保存数据的容器,数据项之间的顺序由添加或删除时的顺序决定,数据项一旦被添加,其相对于前后元素就会一直保持位置不变,诸如此类的数据结构被称为线性数据结构。线性数据结构有两端,称为“左”和“右”,在某些情况下也称为“前”和“后”,当然也可以称为顶部和底部,名称不重要,重要的是这种命名展现出的位置关系表明了数据的组织方式是线性的。这种线性特性和内存紧密相关,因为内存就是一种线性硬件,由此也可以看出软件和硬件是如何关联在一起的。线性数据结构说的并非数据的保存方式,而是数据的访问方式。线性数据结构不一定代表数据项在内存中相邻。以链表为例,虽然其中的数据项可能在内存的各个位置,但访问是线性的。
2、栈
栈就是一种特别有用的线性数据结构,可用于函数调用、网页数据记录等。栈是数据项的有序集合,其中,新项的添加和移除总发生在同一端,这一端称为顶部,与之相对的另一端称为底部。栈的底部很重要,因为栈中靠近底部的项是存储时间最长的,最近添加的项最先被移除。这种排序原则有时被称为后进先出(Last In First Out,LIFO)或先进后出(First In Last Out,FILO),所以较新的项靠近顶部,较旧的项靠近底部。
栈的例子很常见,工地上堆的砖,桌子上摆的书,餐厅里摞在一起的盘子,它们都是栈的物理模型。要想拿到最下面的砖、书、盘子,就必须先把上面的都拿走。
1、栈的抽象数据类型
栈的抽象数据类型由栈的结构和操作定义。如前所述,栈被构造为项的有序集合,其中,项的添加和移除位置被称为顶部。栈的部分操作如下。
●new():创建一个空栈,不需要参数,返回一个空栈。
●push(item):将数据项item添加到栈顶,需要item作为参数,不返回任何内容。
●pop():从栈中删除顶部的数据项,不需要参数,返回数据项,栈被修改。
●peek():从栈中返回顶部的数据项但不删除,不需要参数,不修改栈。
●is_empty():测试栈是否为空,不需要参数,返回布尔值。
●size():返回栈中数据项的数量,不需要参数,返回一个usize型整数。
●iter():返回栈的不可变迭代形式,栈不变,不需要参数。
●iter_mut():返回栈的可变迭代形式,栈可变,不需要参数。
●into_iter():改变栈为可迭代形式,栈被消费,不需要参数。
2、栈的 Rust 代码实现
这里使用集合容器Vec作为栈的底层实现,因为Rust中的Vec提供了有序集合机制和一组操作方法,只需要选定Vec的哪一端是栈顶就可以实现其他操作了。以下栈实现假定Vec的尾部保存了栈的顶部元素,随着栈不断增长,新项将被添加到Vec的末尾。因为不知道所插入数据的类型,所以采用泛型数据类型T。此外,为了实现迭代功能,这里添加了IntoIter、Iter、IterMut三个结构体,以分别完成三种迭代功能。
代码:
/*
* @Description:
* @Author: tianyw
* @Date: 2024-02-15 14:13:35
* @LastEditTime: 2024-02-15 14:56:07
* @LastEditors: tianyw
*/
#[derive(Debug)]
struct Stack<T> {
size: usize, // 栈大小
data: Vec<T>, // 栈数据
}
impl<T> Stack<T> {
// 初始化空栈
fn new() -> Self {
Self {
size: 0,
data: Vec::new(), // 以 Vec 为低层
}
}
fn is_empty(&self) -> bool {
0 == self.size
}
fn len(&self) -> usize {
self.size
}
// 清空栈
fn clear(&mut self) {
self.size = 0;
self.data.clear();
}
// 将数据保存在 Vec 的末尾
fn push(&mut self, val: T) {
self.data.push(val);
self.size += 1;
}
// 将栈顶减 1 后,弹出数据
fn pop(&mut self) -> Option<T> {
if 0 == self.size {
return None;
};
self.size -= 1;
self.data.pop()
}
// 返回栈顶数据引用和可变引用
fn peek(&self) -> Option<&T> {
if 0 == self.size {
return None;
}
self.data.get(self.size - 1)
}
fn peek_mut(&mut self) -> Option<&mut T> {
if 0 == self.size {
return None;
}
self.data.get_mut(self.size - 1)
}
// 以下是为栈实现的迭代功能
// into_iter: 栈改变,成为迭代器
// iter: 栈不变,得到不可变迭代器
// iter_mut:栈不变,得到可变迭代器
fn into_iter(self) -> IntoIter<T> { // into_iter()方法获取了一个迭代器,然后进行迭代
IntoIter(self)
}
fn iter(&self) -> Iter<T> {
let mut iterator = Iter { stack: Vec::new() };
for item in self.data.iter() {
iterator.stack.push(item);
}
iterator
}
fn iter_mut(&mut self) -> IterMut<T> {
let mut iterator = IterMut { stack: Vec::new() };
for item in self.data.iter_mut() {
iterator.stack.push(item);
}
iterator
}
}
// 实现三种迭代功能
struct IntoIter<T>(Stack<T>);
// Iterator 是 Rust 的迭代器 迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。当使用迭代器时,我们无需重新实现这些逻辑。
impl<T: Clone> Iterator for IntoIter<T> { // into_iter()方法获取了一个迭代器,然后进行迭代。
type Item = T;
fn next(&mut self) -> Option<Self::Item> { // 迭代器之所以成为迭代器,是因为实现了Iterator trait。要实现该特征,最主要的就是实现其中的 next 方法,该方法控制如何从集合中取值,最终返回值的类型是关联类型 Item。
if !self.0.is_empty() {
self.0.size -= 1;
self.0.data.pop()
} else {
None
}
}
}
// 'a 生命周期标识 用于帮助编译器检查引用的有效性,避免悬垂引用和使用已被释放的内存。
// 从所有权的角度来理解,就是它可以避免因为Copy或者clone的造成的不必要开销
struct Iter<'a, T: 'a> {
stack: Vec<&'a T>, // 'a 被用在了传参类型 T 上
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T; // 生命周期标识只作用于引用上,且放在&符号之后 如这里的 &'a T
fn next(&mut self) -> Option<Self::Item> {
self.stack.pop()
}
}
struct IterMut<'a, T: 'a> {
stack: Vec<&'a mut T>,
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
self.stack.pop()
}
}
fn main() {
basic();
peek();
iter();
}
// 调用
fn basic() {
let mut s = Stack::new();
s.push("a");
s.push("b");
s.push("c");
println!("basic-----size: {}, {:?}", s.len(), s);
println!("basic-----pop {:?}, size {}", s.pop().unwrap(), s.len());
println!("basic-----empty: {}, {:?}", s.is_empty(), s);
s.clear();
println!("basic-----{:?}", s);
}
fn peek() {
let mut s = Stack::new();
s.push(1);
s.push(2);
s.push(3);
println!("peek-----{:?}", s);
let peek_mut = s.peek_mut();
if let Some(top) = peek_mut {
*top = 4;
}
println!("peek-----top {:?}", s.peek().unwrap());
println!("peek-----{:?}", s);
}
fn iter() {
let mut s = Stack::new();
s.push(1.1);
s.push(2.2);
s.push(3.3);
let sum1: f32 = s.iter().sum();
println!("iter-----sum1:{sum1}");
let mut addend = 0;
for item in s.iter_mut() {
*item += 1.0;
addend += 1;
}
let sum2: f32 = s.iter().sum();
println!("iter-----addend:{addend}");
println!("iter-----sum2:{sum2}");
println!("iter-----{sum1} + {addend} = {sum2}");
}
运行结果: