rust踩雷笔记(4)——刷点Vec相关的题(持续更新)

俗话说,孰能生巧,今天是第六天接触Rust,感觉基础语法和特性没什么问题了(当然如果你整天都学这个可能2天半就够了),但是想达到熟练使用,还需要刷点题。算法我相信能来看rust博客的人都是大牛(只有我最菜),应该只有数据结构的困扰,所以接下来的博客会侧重于数据结构,毕竟咱常见算法都靠C++练得烂熟了,剩下的事情其实是做到把C++题解翻译成rust题解。

hello

      • leetcode 53 最大子数组和
      • leetcode 912——快速排序惨案
      • leetcode 743——网络延迟——Dijkstra
          • 方法一:用向量实现邻接表
            • 补充:小根堆
          • 方法二:实现邻接矩阵

leetcode 53 最大子数组和

这个毫无疑问直接用dp就行了,关键注意事项是with_capacity(n)分配的Vec,你以为它可以用数组下标访问,实际上在它真正push进数据前,下标访问会越界,哪怕dp[0]都不行。

impl Solution {pub fn max_sub_array(nums: Vec<i32>) -> i32 {let n = nums.len();println!("{n}");// 发现一个问题,即便是指定了大小,但是下面dp.len()为0,可能是因为没有元素let mut dp: Vec<i32> = Vec::with_capacity(n);   // dp[i]表示以下标i为结尾的最大连续子数组之和// println!("{}", dp.len());    // 0dp.push(nums[0]);let mut res = dp[0];for i in 1..n {// dp[i] = max(nums[i], nums[i] + dp[i - 1])let t = nums[i].max(nums[i] + dp[i - 1]);dp.push(t);res = res.max(dp[i]);}res}
}

看注释就行了,如果dp.push(nums[0])改为dp[0] = nums[0],那么会报数组下标越界。

基本的使用就是这样,我们继续看别的题

leetcode 912——快速排序惨案

为什么叫惨案,因为我直接用自己的C++解法套用过去,因为语言特性的差异而浪费了两小时。

为了你的安全,请不要嫌弃rust的特性麻烦——鲁迅

这是我C++的写法:

#include<iostream>
using namespace std;const int N = 1e6 + 10;int q[N];
void swap(int &a, int &b){int temp;temp = a;a = b;b = temp;
}void quick_sort(int q[], int l, int r){if (l >= r) return;int temp = q[(l + r) >> 1];int i = l  - 1;int j = r + 1;while(i < j){do i++; while(q[i] < temp);do j--; while(q[j] > temp);if(i < j) swap(q[i], q[j]);}quick_sort(q, l, j);quick_sort(q, j + 1, r);
}int main()
{int n;scanf("%d", &n);for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);quick_sort(q, 0, n - 1);for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);return 0;
}

看看栈溢出的rust写法,当然我还加了好多注释,全是踩到雷的地方。乍一看和C++代码一样,但是要注意usize的问题,详见注释。

impl Solution {pub fn quick_sort(a: &mut Vec<i32>, l: usize, r: usize) {// 不写递归边界会报错:thread 'main' has overflowed its stack (solution.rs)if l >= r {return ;}// 对a的下标l到r之间进行快速排序// 栈溢出的原因在这里,都是usize类型,如果l为0,那么i的值不会是-1而是很大的数// 而且只能是usize,因为需要下标访问let mut i = l - 1;let mut j = r + 1;let mid = (l + r) / 2;while i < j {// while *a[l] < *a[mid] {	// 这样是错的,a是引用但是a[i]不是,*优先级没有[]高i += 1;while a[i] < a[mid] {i += 1;}j -= 1;while a[j] > a[mid] {j -= 1;}if i < j {let temp = a[i];a[i] = a[j];a[j] = temp;}}println!("i:{}, j:{}", i, j);// 递归要加上Solution::或者Self::// Solution::quick_sort(a, l, j);// Solution::quick_sort(a, j + 1, r);Self::quick_sort(a, l, j);Self::quick_sort(a, j + 1, r);}pub fn sort_array(nums: Vec<i32>) -> Vec<i32> {// 用这行试验了一下,move occurs because `nums` has type `Vec<i32>`, which does not implement the `Copy` trait// 也就是说所有权转移给了nums1// let mut nums1 = nums;// 这句是必要的,入参那里的nums不是mutable的,所以后面&mut nums会报错。创建了新的mutable nums并将入参所有权转移过来let mut nums = nums;// 早上太困了,简单写个快速排序// 报错nums.len()这里还borrowed as immutable,不能同时是mutable和immutable的引用// Solution::quick_sort(&mut nums, 0, nums.len() - 1); let len = nums.len();Solution::quick_sort(&mut nums, 0, len - 1); nums}
}

改正之后的代码,对usize和i32进行合适的类型转换,

⚠️i32和usize不能直接相互赋值、参与运算

但是需要注意的是,let mid = (l + r) / 2 as usize;,其中l和r都是i32,你觉得这句话会报错么?
当然会!2 as usize会被当成一个整体,于是变成i32 / usize,报错。

impl Solution {pub fn quick_sort(a: &mut Vec<i32>, l: i32, r: i32) {// 不写递归边界会报错:thread 'main' has overflowed its stack (solution.rs)if l >= r {return ;}// 对a的下标l到r之间进行快速排序let mut i = l - 1;let mut j = r + 1;let mid = a[((l + r) / 2) as usize];while i < j {i += 1;while a[i as usize] < mid {i += 1;}j -= 1;while a[j as usize] > mid {j -= 1;}if i < j {let temp = a[i as usize];a[i as usize] = a[j as usize];a[j as usize] = temp;}}// Self或者Solution都可以// Solution::quick_sort(a, l, j);// Solution::quick_sort(a, j + 1, r);Self::quick_sort(a, l, j);Self::quick_sort(a, j + 1, r);}pub fn sort_array(nums: Vec<i32>) -> Vec<i32> {let mut nums = nums;let len = nums.len();Solution::quick_sort(&mut nums, 0, (len - 1) as i32); nums}
}

小结,快速排序不难,麻烦的是语言特性相关的东西:
(1)注意[]下标访问只能用usize不能用i32,下标访问比*优先级高,如果a是引用&i32,但a[i]是i32(自动“解引用”了),此时不要自作聪明用*a[i],会报错对i32没法解引用
(2)usize和i32不能相互赋值,所以xxx as usize和xxx as i32用起来,但注意优先级问题:(a + b) / 2 as usize,这是先把2转为usize
(3)Vec<i32>依旧是没有实现copy trait的,所以它的赋值会转移所有权。当然这句话和本题无关,和本题有关的是方法签名:
pub fn sort_array(nums: Vec<i32>) -> Vec<i32>,如果你要改变nums,然后返回它,由于它是immutable的,所以你需要let mut nums = nums,重新创建一个变量绑定老nums的值(当然这么做老nums的值就被move掉了),然后改变新nums并返回
(4)不要抱怨rust的特性怎么这么复杂,我一开始套用自己的解法,因为要把变量定义为i32并在若干地方加as usize,我觉得这样不优雅,就试图改变解法,结果发明了几个错误的快排写法。非常蛋疼,假如我没有嫌弃rust的写法麻烦,而是理解这是为了程序安全,就不会浪费时间了!

leetcode 743——网络延迟——Dijkstra

看下图的数据结构,无非就是邻接表和邻接矩阵,这个都可以用数组或者向量来实现。

方法一:用向量实现邻接表
补充:小根堆

注意用到了小根堆

use std::collections::BinaryHeap;
use std::cmp::Reverse;

详情见下面代码,反正push的时候如果用Reverse包起来,那就是小根堆,不包就是大根堆。
如果是小根堆,那么你heap.pop().unwrap_or(Reverse((0, 0)))查出来的是Reverse(元素),注意不是引用(加unwrap是因为pop()查出来是Some(Reverse(元素))或者None),那么需要自己解构一下。

use std::collections::BinaryHeap;
use std::cmp::Reverse;const MAX_VALUE: i32 = 100 * 100 + 7;
impl Solution {pub fn network_delay_time(times: Vec<Vec<i32>>, n: i32, k: i32) -> i32 {let (n, k) = (n as usize, k as usize);// 二维数组注意显式指定类型let mut graph = vec![vec![]; n + 1];    // graph[i]表示节点i的邻接表,注意i是1到n// 初始化图// 这道题用这里总结下遍历数组方法大全for i in 0..times.len() {// times[i] = [u,v,w],表示一条边// 二维数组除了用[][]定位元素,还有没有更好的方法// 坑死了,times是i32类型向量;坑死了+1,不要把w变成usize,后面它要和i32运算啊喂!let (u, v, w) = (times[i][0] as usize, times[i][1] as usize, times[i][2]);// 所有用于数组下标索引的都必须是usizegraph[u].push((v, w));}let mut dist = vec![MAX_VALUE; n + 1];  // dist[i]表示到节点i的最小距离let mut state = vec![0; n + 1];     // state[i]=0表示节点i没被确认最短路let mut cnt = 0;    // 表示有多少节点确认了最短路let mut heap = BinaryHeap::new();let mut res = 0;dist[k] = 0;heap.push(Reverse((0, k)));while heap.len() != 0 {// heap.pop直接获取元素本身,而不是元素的引用let Reverse(t) = heap.pop().unwrap_or(Reverse((0, 0)));let (d, u) = (t.0, t.1);// 如果u已经被确认最短路,那就直接跳过if state[u] == 1 {continue;}state[u] = 1;cnt += 1;res = res.max(dist[u]);// 更新u的所有下一个点for i in 0..graph[u].len() {// graph[u] = [(v,w),()...]let (v, w) = graph[u][i];if state[v] == 1 {continue;}if dist[u] + w < dist[v] {dist[v] = dist[u] + w;heap.push(Reverse((dist[v], v)));}}}if cnt != n {-1} else {res}}
}

mark一下rust圣经上的遍历语句
在这里插入图片描述
还有一个enumerate,拿的是下标和引用:

let mut v = vec![1, 3, 2];	// Vec<i32>
for (i, va) in v.iter().enumerate() {}

i是usize,va是&i32

方法二:实现邻接矩阵

这个我测了一下:
在这里插入图片描述
(这图怎么粘上来这么大,怀念以前可以直接调整图片大小的时候)

这个可以成功打印出3x2的矩阵,元素都是1.

To be continued…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/100409.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习基本理论下篇:(梯度下降/卷积/池化/归一化/AlexNet/归一化/Dropout/卷积核)、深度学习面试

深度学习基本理论上篇&#xff1a;&#xff08;MLP/激活函数/softmax/损失函数/梯度/梯度下降/学习率/反向传播&#xff09; 深度学习基本理论上篇&#xff1a;&#xff08;MLP/激活函数/softmax/损失函数/梯度/梯度下降/学习率/反向传播&#xff09;、深度学习面试_会害羞的杨…

SOPC之NIOS Ⅱ实现电机转速PID控制

通过FPGA开发板上的NIOS Ⅱ搭建电机控制的硬件平台&#xff0c;包括电机正反转、编码器的读取&#xff0c;再通过软件部分实现PID算法对电机速度进行控制&#xff0c;使其能够渐近设定的编码器目标值。 一、PID算法 PID算法&#xff08;Proportional-Integral-Derivative Algo…

还不知道怎么提示LLM?ChatGPT提示入门

文章目录 简介&#xff1a;什么是人工智能&#xff1f;什么是提示过程&#xff1f;为什么会出现这样的差异&#xff1f; 为什么需要提示过程&#xff1f;1) 文章摘要2) 数学问题求解 如何进行提示过程&#xff1f;角色提示&#xff1a;多范例提示&#xff1a;无范例提示单范例提…

龙讯旷腾PWmat已部署至曙光智算平台

编者荐语&#xff1a; 近期&#xff0c;龙讯旷腾核心产品PWmat已成功部署至曙光智算AC.sugon.com平台&#xff0c;可为用户提供包括分子建模、第一性原理计算、数据可视化等在内的完备的超级计算云服务&#xff0c;让大家能够轻松上手具有完全自主知识产权的大尺度高性能材料计…

常见前端面试之VUE面试题汇总一

1. Vue 的基本原理 当 一 个 Vue 实 例 创 建 时 &#xff0c; Vue 会 遍 历 data 中 的 属 性 &#xff0c; 用 Object.defineProperty &#xff08; vue3.0 使 用 proxy&#xff09; 将 它 们 转 为 getter/setter&#xff0c;并且在内部追踪相关依赖&#xff0c;在属性被访…

【CSS】CSS 布局——常规流布局

<h1>基础文档流</h1><p>我是一个基本的块级元素。我的相邻块级元素在我的下方另起一行。</p><p>默认情况下&#xff0c;我们会占据父元素 100%的宽度&#xff0c;并且我们的高度与我们的子元素内容一样高。我们的总宽度和高度是我们的内容 内边距…

fastapi发布web配置页面

fastapi发布web配置页面 FastAPI 是一个基于 Python 的快速 Web 开发框架&#xff0c;它提供了许多功能来简化 Web 开发过程。其中一个重要的功能是能够轻松地创建 API 文档页面。 在 FastAPI 中&#xff0c;可以使用 OpenAPI 和 Swagger 来创建 API 文档页面。下面是一个简单…

图像降采样的计算原理:F.interpolate INTER_AREA

一、F.interpolate——数组采样操作 torch.nn.functional.interpolate(input, size=None, scale_factor=None, mode=nearest, align_corners=None, recompute_scale_factor=None) 功能:利用插值方法,对输入的张量数组进行上\下采样操作,换句话说就是科学合理地改变数组的尺…

一套基于C#语言开发的LIMS实验室信息管理系统源码

实验室信息管理系统&#xff08;LIMS)是指帮助实验室组织和管理实验数据的计算机软件系统&#xff0c;它将实验室操作有机地组织在一起&#xff0c;以满足实验室工作流程的所有要求。它能以不同的方式支持实验室的工作&#xff0c;从简单的过程(如样品采集和入库)到复杂的流程(…

微信小程序使用云存储和Markdown开发页面

最近想在一个小程序里加入一个使用指南的页面&#xff0c;考虑到数据存储和减少页面的开发工作量&#xff0c;决定尝试在云存储里上传Markdown文件&#xff0c;微信小程序端负责解析和渲染。小程序端使用到一个库Towxml。 Towxml Towxml是一个可将HTML、Markdown转为微信小程…

ESB是什么?传统ESB升级该怎么选?

ESB的由来 下面这张图&#xff0c;稍微了解些IT集成的朋友应该不陌生。 随着信息化发展不断深入&#xff0c;企业在不同的阶段引入了不同的应用、系统和软件。这些原始的应用系统互不连通&#xff0c;如同一根根独立的烟囱。 但是企业业务是流程化的&#xff0c;这就需要业务…

2023 网络建设与运维 X86架构计算机操作系统安装与管理题解

任务描述: 随着信息技术的快速发展,集团计划2023年把部分业务由原有的X86架构服务器上迁移到ARM架构服务器上,同时根据目前的部分业务需求进行了部分调整和优化。 一、X86架构计算机操作系统安装与管理 1.PC1系统为ubuntu-desktop-amd64系统(已安装,语言为英文),登录用户…

记一次布尔盲注漏洞的挖掘与分析

在上篇文章记一次由于整型参数错误导致的任意文件上传的漏洞成因的分析过程中&#xff0c;发现menu_id貌似是存在注入的。 public function upload() {$menu_id $this->post(menu_id);if ($id) {$where "id {$id}";if ($menu_id) {$where . " and menu_id…

「我的编程笔记」——记录学习中的代码、函数、概念等

文章目录 每日一句正能量前言常用的代码登录存储 特定函数MD5加密 复杂概念1. 多线程2. 集合类3. 异常处理4 泛型5 反射 特定功能1. 文件操作2. 网络通信3. 图形绘制4. 数据库操作5. 多媒体处理 后记 每日一句正能量 不管昨天、今天、明天&#xff0c;能豁然开朗就是最美好的一…

5.8.webrtc事件处理基础知识

在之前的课程中呢&#xff0c;我向你介绍了大量web rtc线程相关内容&#xff0c;今天呢&#xff0c;我们来看一下线程事件处理的基本知识。首先&#xff0c;我们要清楚啊&#xff0c;不同的平台处理事件的API是不一样的&#xff0c;这就如同我们当时创建线程是类似的&#xff0…

C#-Tolewer和ToUpper的使用

目录 简介: 好处:​ 过程: 总结&#xff1a; 简介: 字符串是不可变的&#xff0c;所以这些函数都不会直接改变字符串的内容&#xff0c;而是把修改后的字符串的值通过函数返回值的形式返回。 ToLower和ToUpper是字符串处理函数&#xff0c;用于将字符中的英文字母转换为小…

并查集 size 的优化(并查集 size 的优化)

目录 并查集 size 的优化 Java 实例代码 UnionFind3.java 文件代码&#xff1a; 并查集 size 的优化 按照上一小节的思路&#xff0c;我们把如下图所示的并查集&#xff0c;进行 union(4,9) 操作。 合并操作后的结构为&#xff1a; 可以发现&#xff0c;这个结构的树的层相对…

Spring练习---28 (用户表和角色表分析,角色列表展示,角色层和Dao层的设置,页面展示操作)

84、下面进入我们的业务层面&#xff0c;进入我们的业务层面我们先分析一个东西&#xff0c;我们要分析用户和角色的关系&#xff0c;因为我们只有在分析完用户和角色之间的关系后&#xff0c;我们才知道表的关系&#xff0c;实体的关系 85、现在我们先画一张表&#xff0c;分析…

嵌入式设备应用开发(qt界面开发)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 linux界面开发有很多的方案可以选。比如说lvgl、minigui、ftk之类的。但是,这么多年来,一直屹立不倒的还是qt。相比较其他几种方案,qt支持多个平台,这里面就包括了linux平台。此…

《Linux从练气到飞升》No.16 Linux 进程地址空间

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…