一起学Rust-实战leetcode(四)
- 2019 年 10 月 6 日
- 笔记
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储一位数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/add-two-numbers
这是来源于leetcode的一道题 “两数相加”,我们使用Rust来实现。
本次实战目的:
了解Rust中 Box<T> 指针在链表中的使用,了解结构体方法,学习 Option<T> 的一些常用方法以及所有权的复习。
简单分析:
按照往常一样,首先对题目进行简单分析,题目给出了两个前提(两个非空链表,除了单个的0,数字开头不会是0),这样可以暂不考虑这道题目的异常情况。
从题目可看出,是将相对应的位置数值相加,同时保持正常的进位,每一个节点都是个位数。每一次的进位如果存在就加到下一个节点的和中。
计算链表:每次循环中将计算并创建的节点,赋值到上一个节点的next。这里会涉及两个引用,一个是整个链表的所有者变量,地址指向的是链表起始的位置,另外一个是对于链表尾部的next的一个可变引用,使用可以将新的节点追加到链表的尾部。
结束:当输入的链表全部遍历结束,并且结尾没有进位的情况下,链表计算完成。
注意:如果出现不等长的输入,空缺的节点需要使用0代替。
本题并不难,例如在PHP中就非常容易实现,比较麻烦的是在Rust中实现会有意想不到的问题,例如所有权的问题等。
下面是题干给出的结构与方法。
#[derive(PartialEq, Eq, Clone, Debug)] pub struct ListNode { pub val: i32, pub next: Option<Box<ListNode>>, } impl ListNode { #[inline] fn new(val: i32) -> Self { ListNode { next: None, val, } } }
下面编写一下代码:
fn add_two_numbers(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> { // 参数改为可变值 let (mut l1, mut l2) = (l1, l2); // 初始化一个为0的起始节点。 let mut ret = Some(Box::new(ListNode::new(0))); let mut tmp = &mut ret; // 进位值,1或0 let mut up = 0; while l1.is_some() || l2.is_some() || up > 0 { // 获取节点的值,如果没有则为0 let v1 = if let Some(l) = &l1 { l.val } else { 0 }; let v2 = if let Some(l) = &l2 { l.val } else { 0 }; // 计算节点的和需要额外加进位的值 let sum = v1 + v2 + up; // up就是sum/10的整数商 up = sum / 10; let val = Box::new(ListNode::new(sum % 10)); //创建内部可变并获取内部值的next ,赋值为当前计算的节点。 tmp.as_mut().unwrap().next = Some(val); //将可变的next节点指针赋值给尾部变量。 tmp = &mut tmp.as_mut().unwrap().next; // 将节点向下遍历 l1 = if l1.is_some() { l1.unwrap().next } else { None }; l2 = if l2.is_some() { l2.unwrap().next } else { None } } //ret第一个节点是与结果无关的,所以不需要,只需要返回next的值 ret.unwrap().next }
解释:
Box::new(x:T) ,将x值存储到堆内存中,而Box的变量指向此堆内存,对于递归的结构体来说用处非常大,由于结构体需要计算大小,所以通过Box<T>来固定结构体成员的大小,通过指针完成数据的连接。
is_some() , is_none() 是Option<T>枚举的方法,返回布尔值,是用于判断枚举值是Some(T)还是None值。
unwrap() 是用于获取Some(T)内的值,如果是None则会panic退出,需要注意的是,unwrap会获取调用者的所有权,也就是调用后,调用者变量会失效。与之类似的方法 unwrap_or(def:T) 提供None时的默认值,不会发生panic, unwrap_or_else(f: F) 同样是获取Some内值,并会接受一个处理None情况的函数,函数需要返回与Some内值相同类型的值。这三种方法都会获取调用者的所有权。
as_mut() 将可变的Option<T>枚举引用转换为内部值的可变引用,
Converts from `&mut Option<T>` to `Option<&mut T>`.
as_mut() 需要一个可变的Option引用来调用,并不会转移所有权,并返回一个新的内部可变的Option。
impl 用于定义方法或实现trait,方法中的参数如果不存在self,则需要使用 :: 来调用,如果第一个参数使用 self , &self , &mut self 则需要使用点运算符来调用。