前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一起学Rust-实战leetcode(四)

一起学Rust-实战leetcode(四)

作者头像
江湖安得便相忘
发布2019-09-24 16:16:15
9250
发布2019-09-24 16:16:15
举报

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储一位数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

代码语言:javascript
复制
输入:(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中实现会有意想不到的问题,例如所有权的问题等。

下面是题干给出的结构与方法。

代码语言:javascript
复制
#[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,
        }
    }
}

下面编写一下代码:

代码语言:javascript
复制
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>枚举引用转换为内部值的可变引用,

代码语言:javascript
复制
Converts from `&mut Option<T>` to `Option<&mut T>`.

as_mut() 需要一个可变的Option引用来调用,并不会转移所有权,并返回一个新的内部可变的Option。

impl 用于定义方法或实现trait,方法中的参数如果不存在self,则需要使用 :: 来调用,如果第一个参数使用 self , &self , &mut self 则需要使用点运算符来调用。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可回收BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档