rust学习笔记10-智能指针与142.环形链表II

1.智能指针Rc<T>

再上一篇我们已经接触过一个智能指针Box<T>,并且完成了翻转链表,但是Box 是一个独占所有权的智能指针,意味着不能创建环形链表,因为环形链表势必有2个节点同时指向一个区域,因此就要采用可以共享所有权的Rc<T>智能指针

Rc<T> 智能指针

Rc<T>(引用计数指针)允许多个所有者共享数据,它使用引用计数来跟踪数据的所有者数量,并在所有者数量为零时释放数据。

Rc<T> 适用于单线程环境下的数据共享。

   具体使用

use std::rc::Rc;

let data = Rc::new(42); // 引用计数 = 1

{
    let clone1 = Rc::clone(&data); // 引用计数 = 2
    let clone2 = Rc::clone(&data); // 引用计数 = 3
    
    println!("{}", *clone1); // 输出 42
} // 引用计数减少到 1

println!("Final count: {}", Rc::strong_count(&data)); // 输出 1

RefCell<T> 智能指针

RefCell<T> 允许在运行时检查借用规则,它使用内部可变性来提供了一种安全的内部可变性模式,允许在不可变引用的情况下修改数据。

但是,RefCell<T> 只能用于单线程环境。

use std::cell::RefCell;

let cell = RefCell::new(42);

// 不可变作用域中修改值
{
    let mut borrow = cell.borrow_mut();
    *borrow += 10;
}

println!("{}", cell.borrow()); // 输出 52

Rc<RefCell<T>> 组合使用

优势
共享所有权 + 内部可变性:既允许多个所有者,又支持修改数据

动态借用检查:运行时管理借用规则

定义链表结构

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct ListNode {
    val: i32,
    next: Option<Rc<RefCell<ListNode>>>,
}

impl ListNode {
    fn new(val: i32) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(ListNode { val, next: None }))
    }
}

142. 环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

    示例 1:

    输入:head = [3,2,0,-4], pos = 1
    输出:返回索引为 1 的链表节点
    解释:链表中有一个环,其尾部连接到第二个节点。
    

    示例 2:

    输入:head = [1,2], pos = 0
    输出:返回索引为 0 的链表节点
    解释:链表中有一个环,其尾部连接到第一个节点。
    

    示例 3:

    输入:head = [1], pos = -1
    输出:返回 null
    解释:链表中没有环。
    

    提示:

    • 链表中节点的数目范围在范围 [0, 104] 内
    • -105 <= Node.val <= 105
    • pos 的值为 -1 或者链表中的一个有效索引

    进阶:你是否可以使用 O(1) 空间解决此题?

    (1)先解决为空问题

            这次不仅要判断节点是否为空,还要处理返回值可能为空的情况,怎么办,判断节点为空还是之前的Option枚举类型,返回值可以自定义一个Result枚举类型

    enum CycleResult {
        NoCycle, // 表示链表中没有环
        CycleStart(Rc<RefCell<ListNode>>), // 表示链表中有环,并返回环的起始节点
    }

    重要说明:本题目前力扣还不支持提交,因此只能本地运行测试。

    (1)完整解题

    use std::rc::Rc;
    use std::cell::RefCell;
    
    #[derive(Debug)]
    struct ListNode {
        val: i32,
        next: Option<Rc<RefCell<ListNode>>>,
    }
    
    impl ListNode {
        fn new(val: i32) -> Rc<RefCell<Self>> {
            Rc::new(RefCell::new(ListNode { val, next: None }))
        }
    }
    
    enum CycleResult {
        NoCycle, // 表示链表中没有环
        CycleStart(Rc<RefCell<ListNode>>), // 表示链表中有环,并返回环的起始节点
    }
    pub fn detect_cycle(head: Option<&Rc<RefCell<ListNode>>>) -> CycleResult {
        let mut fast = head.cloned(); // 将 &Rc 转换为 Rc
        let mut slow = head.cloned();
        // 判断slow和fast是否为空
        while let (Some(slow_node), Some(fast_node)) = (slow.as_ref(), fast.as_ref()) {
            println!("慢指针: {}, 快指针: {}", slow_node.borrow().val, fast_node.borrow().val);
            //慢指针走一步
            slow = {
                let next = slow_node.borrow().next.clone();
                next
            };
    
            // 快指针走两步
            fast = {
                let next1 = fast_node.borrow().next.clone();
                if let Some(f2) = next1 {
                    let next2 = f2.borrow().next.clone();
                    next2
                } else {
                    None
                }
            };
    
            if fast.is_none() {
                return CycleResult::NoCycle; // 如果 fast 为 None,则没有环
            }
    
            // 判断是否快慢指针是否相遇,相遇代表有环,跳出循环
            if let (Some(slow_node2), Some(fast_node2)) = (slow.as_ref(), fast.as_ref()) {
                println!("慢指针: {}, 快指针: {}", slow_node2.borrow().val, fast_node2.borrow().val);
                if Rc::ptr_eq(slow_node2, fast_node2) {
                    println!("有环");
                    break; // 找到相遇点
                }
            }
        }
    
        if slow.is_none() || fast.is_none() {
            return CycleResult::NoCycle;
        }
        //寻找环的起始节点
        let mut index1 = head.cloned();
        let mut index2 = slow;
        while let (Some(p1), Some(p2)) = (index1.as_ref(), index2.as_ref()) {
            if Rc::ptr_eq(p1, p2) {
                //println!("环的起始节点{}",p1.borrow().val);
                return CycleResult::CycleStart(p1.clone()); // 找到环的起始节点
            }
            // 使用新的作用域释放借用
            index1 = {
                let next = p1.borrow().next.clone();
                next
            };
            index2 = {
                let next = p2.borrow().next.clone();
                next
            };
        }
    
        return CycleResult::NoCycle;
    
    }
    
    fn main() {
        let mut node1 = ListNode::new(1);
        let node2 = ListNode::new(2);
        let node3 = ListNode::new(3);
        let node4 = ListNode::new(4);
        let node5 = ListNode::new(5);
    
        // 构建链表:1 -> 2 -> 3 -> 4 -> 5
        node1.borrow_mut().next = Some(node2.clone());
        node2.borrow_mut().next = Some(node3.clone());
        node3.borrow_mut().next = Some(node4.clone());
        node4.borrow_mut().next = Some(node5.clone());
    
        // 创建环:5 -> 3
        node5.borrow_mut().next = Some(node3.clone());
        // 调用 detect_cycle 函数
        let result = detect_cycle(Some(&node1));
    
        match result {
            CycleResult::NoCycle => println!("链表中没有环"),
            CycleResult::CycleStart(result) => println!("环的起始节点是: {}", result.borrow().val),
        }
    }
    

    无环测试 注释掉//node5.borrow_mut().next = Some(node3.clone());

    总结,本题有一定难度,一开始想放弃,原因有二,一个是智能指针还没完全掌握,在一个力扣还不能提交。但由于之前面试碰到过,还是咬牙坚持做出来。本题主要关键点,一个是智能指针Rc<RefCell<T>> 组合使用,在一个处理返回值,这次用了Result枚举类型,还要一个就力扣不能提交本地测试(这个建议用AI生成测试用例,不必自己去写)

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值