Rust踩雷笔记(7)——两个链表题例子初识裸指针

news/2025/2/19 17:03:13/

目录

      • leetcode 234
      • leetcode 19

leetcode 234

题目在这https://leetcode.cn/problems/palindrome-linked-list/,leetcode 234的回文链表,思路很简单,就是fast和slow两个指针,fast一次移动两个、slow一次一个,最后slow指向的链表反转后和head比较就行了。

很简单一题,但考虑一个问题:slow和fast是什么形式?由于rust有所有权机制,所以slow和fast只能是借用,并且还不能是可变借用(可变借用只能有一个、可变借用和不可变借用不能同时存在)。

所以slow和fast只能是两个不可变借用,直接放上代码:

// Definition for singly-linked list.
// #[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
//     }
//   }
// }
impl Solution {pub fn reverse(mut head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {let mut prev = None;while let Some(mut node) = head {head = node.next;node.next = prev;prev = Some(node);}prev}pub fn is_palindrome(head: Option<Box<ListNode>>) -> bool {// 快慢指针,fast一次移动2个,slow一次移动1个,slow会停留在中间偏后的位置// 反转slow为头结点的链表// 比较head和slow两个链表,直到head的长度达到即停止let p = head.as_ref().unwrap();if p.next.is_none() {return true;}let mut head = head;let mut slow = &head;let mut fast = &head;while slow.is_some() && fast.is_some() {slow = &(slow.as_ref().unwrap().next);fast = &(fast.as_ref().unwrap().next);if fast.is_none() {break;}fast = &(fast.as_ref().unwrap().next);}// let s =  slow  as *const Option<Box<ListNode>> as * mut  Option<Box<ListNode>>;let s = slow as *const Option<Box<ListNode>> as *mut Option<Box<ListNode>>;let mut head2 = unsafe {(*s).take()};head2 = Solution::reverse(head2);let mut flag = true;while let (Some(node1), Some(node2)) = (head.as_ref(), head2.as_ref()) {if node1.val != node2.val {flag = false;break;}head = head.unwrap().next;head2 = head2.unwrap().next;}flag}
}

主要注意代码中的

let s = slow as *const Option<Box<ListNode>> as *mut Option<Box<ListNode>>;
let mut head2 = unsafe {(*s).take()
};

这里我的本意是写成这样:

let mut head2 = slow.take();

但是slow是不可变借用,这里就会报错:

cannot borrow `*slow` as mutable, as it is behind a `&` reference
`slow` is a `&` reference, so the data it refers to cannot be borrowed as mutable

大意就是slow不是可变借用,take()的作用是拿走物主对某个东西的所有权,然后将所有权交给另一个人。你借一个人的东西,并且不被允许改变这个东西,那么你肯定不能把这个东西的所有权扔给别人对吧。

这个时候就需要裸指针的概念了,如果不会请移步:
https://kaisery.github.io/trpl-zh-cn/ch19-01-unsafe-rust.html
链接中截图关键内容:
在这里插入图片描述
注意*const*mut是不可变、可变两种裸指针,星号不是解引用,而是类型的一部分。⚠️注意是将不可变引用变成*const类型的裸指针,可变引用变成*mut类型的裸指针,所以前面的代码里写的是:

let s = slow as *const Option<Box<ListNode>> as *mut Option<Box<ListNode>>;

slow是不可变引用&Option<Box<ListNode>>,所以先转换为*const Option<Box<ListNode>>,再转换为*mut Option<Box<ListNode>>类型。

然后要在unsafe代码块中使用它,记得写法是(*s).take()拿到所有权赋给head2

let mut head2 = unsafe {(*s).take()
};

至此head2就是slow引用的那个节点了。

leetcode 19

题目是删除链表中倒数第n个节点,要求一趟遍历。

这里也可以使用裸指针,只要是如下场景都可以考虑裸指针:
(1)&和&mut同时出现,又不可避免。那么如果已经有了&,还需要一个&mut的话,可以创建裸指针mut;
(2)需要通过&不可变引用进行改变,那么可以将&转换为
const,再转换为*mut,此时就和&mut作用一致了。

// Definition for singly-linked list.
// #[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
//     }
//   }
// }
impl Solution {pub fn remove_nth_from_end(head: Option<Box<ListNode>>, n: i32) -> Option<Box<ListNode>> {// 从头结点开始向后走n - 1步let mut dummy = Some(Box::new(ListNode::new(0)));dummy.as_mut().unwrap().next = head;let mut p_tail = &(dummy.as_ref().unwrap().next);let mut cnt = 0;    // 向后走了几步while cnt < n - 1 {cnt += 1;p_tail = &(p_tail.as_ref().unwrap().next);}let mut delete_next = &dummy;while p_tail.as_ref().unwrap().next.is_some() {p_tail = &(p_tail.as_ref().unwrap().next);delete_next = &(delete_next.as_ref().unwrap().next);}// 至此,我们只需要删除delete_next节点的下一个节点// 然后返回dummy的下一个节点即可// 通过裸指针拿到delete_next节点的下一个的下一个节点的所有权let mut need_take = &(delete_next.as_ref().unwrap().next);need_take = &(need_take.as_ref().unwrap().next);// need_take是不可变引用,先拿到*mut类型的裸指针,便可拿到need_take引用的节点所有权let mut temp1  = need_take as *const Option<Box<ListNode>> as *mut Option<Box<ListNode>>;let mut temp = unsafe {(*temp1).take()};// 我们需要将need_take的所有权赋给delete_next节点的next,所有权现在给了temp// delete_next是不可变引用,我们要修改它引用的节点的next,就可以通过可变裸指针// 不可变引用需要先转换为*const再转换为*mutlet mut delete_next_temp = delete_next as *const Option<Box<ListNode>> as *mut Option<Box<ListNode>>;unsafe {(*delete_next_temp).as_mut().unwrap().next = temp;}dummy.unwrap().next}
}

http://www.ppmy.cn/news/1118331.html

相关文章

企业蓄电池怎么实时监测?这个方法最简单使用!

在这个数字时代&#xff0c;企业对电力的依赖性愈发显著&#xff0c;这使得电池系统成为维持业务连续性的不可或缺的一环。 蓄电池监控不仅有助于实时跟踪电池系统的性能和状态&#xff0c;还有助于预测问题&#xff0c;提前采取措施以防止电力中断。它还可以帮助企业降低能源成…

leetcode646. 最长数对链(java)

最长数对链 题目描述贪心解法二 动态规划 dp 题目描述 难度 - 中等 leetcode646. 最长数对链(java) 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i] [lefti, righti] 且 lefti < righti 。 现在&#xff0c;我们定义一种 跟随 关系&#xff0c;当且仅…

成都瀚网科技有限公司:抖店平台买家怎么修改评价?

在抖音电商平台上&#xff0c;买家的评价对店铺的声誉和销售业绩有着重要影响。然而&#xff0c;有时买家可能会因为某些原因想要修改之前的评价。那么&#xff0c;抖店平台上的买家如何修改评价呢&#xff1f;修改评价对店铺有什么影响&#xff1f;本文将介绍买家如何修改评价…

AUTOSAR汽车电子嵌入式编程精讲300篇-基于AUTOSAR架构的AT控制系统研究与实现

目录 前言 国内外研究现状 国外研究现状 国内研究现状 2 AUTOSAR规范及开发流程

科技资讯|Canalys发布全球可穿戴腕带设备报告,智能可穿戴增长将持续

市场调查机构 Canalys 近日发布报告&#xff0c;表示 2023 年第 2 季度全球可穿戴腕带设备出货量达 4400 万台&#xff0c;同比增长了 6%。 主要归功于其亲民的价格以及消费者对价位较高的替代品仍持谨慎态度&#xff0c;基础手环市场尽管与去年同期相比有所下降&#xff0c;…

SpringBoot+若依+图片导出

前言 本文基于若依框架&#xff0c;实现excel中图片导出功能。 自定义导出Excel数据注解 public enum ColumnType{NUMERIC(0), STRING(1), IMAGE(2);private final int value;ColumnType(int value){this.value value;}public int value(){return this.value;}} 工具类中设置…

蓝桥杯打卡Day12

文章目录 接龙数列冶炼金属 一、接龙数列OJ链接 本题思路:本题是一道经典的dp问题&#xff0c;设第i个数的首位数字是first&#xff0c; 末位数字是last。因为第i个数只可能加到一个以first结尾的接龙数列中使得这个接龙数列长度加1并且结尾数字变成last.所以状态转移方程为d…

高级运维学习(八)Ceph 概述与部署

ceph概述 ceph可以实现的存储方式&#xff1a; 块存储&#xff1a;提供像普通硬盘一样的存储&#xff0c;为使用者提供“硬盘”文件系统存储&#xff1a;类似于NFS的共享方式&#xff0c;为使用者提供共享文件夹对象存储&#xff1a;像百度云盘一样&#xff0c;需要使用单独的客…