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

这篇具有很好参考价值的文章主要介绍了rust踩雷笔记(4)——刷点Vec相关的题(持续更新)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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());    // 0
        dp.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]);
            // 所有用于数组下标索引的都必须是usize
            graph[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圣经上的遍历语句
rust踩雷笔记(4)——刷点Vec相关的题(持续更新),Rust从入门到入门,rust,笔记,开发语言
还有一个enumerate,拿的是下标和引用:

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

i是usize,va是&i32

方法二:实现邻接矩阵

这个我测了一下:
rust踩雷笔记(4)——刷点Vec相关的题(持续更新),Rust从入门到入门,rust,笔记,开发语言
(这图怎么粘上来这么大,怀念以前可以直接调整图片大小的时候)

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

To be continued…文章来源地址https://www.toymoban.com/news/detail-662434.html

到了这里,关于rust踩雷笔记(4)——刷点Vec相关的题(持续更新)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • rust踩雷笔记(2)——一道hard带来的思考[哈希表、字符串、滑动窗口]

    今天被一道hard恶心坏了,算法不难,用C++几分钟的事。用rust主要还是缺乏对语言的熟练度,记录一下,主要的坑在下面这个操作: 对String取其中某个位置的char。 可能你会有疑问:这不是直接nth()取就行了么。没错,我看有些题解也是这样做的,但是那样会在某些字符长度很

    2024年02月12日
    浏览(26)
  • Rust系列(四) trait备忘录(持续更新)

    上一篇:Rust系列(三) 类型系统与trait 基于官方文档进行简单学习记录,保证所有示例是可运行的基本单元。测试 rust 程序除了使用官方的 playground 之外,还可以通过定义 [[example]] 来运行程序。 用于 不可变对象 的解引用操作,语法类似 *v 。 官方文档: https://doc.rust-lang.org

    2024年02月14日
    浏览(25)
  • Vue2相关面试题(持续更新)

    前言 目前这套面试题只适合 初级前端 ,后面会进行深层次补充和拓展以及Vue2源代码的讲解(虽然Vue2今年开始不维护了,但是我的面试题不会止步,冲冲冲)。 在面试的过程中,一定要清楚哪些该说哪些不该说,如果一个知识点不太清楚,就不要做过多的解释,一笔带过就

    2024年02月04日
    浏览(23)
  • [汇总]计算机专业相关证书大全(持续更新...)

    所有数据 来源于网络 , 每个证书数据来源会附在小节标题后 。文章内容 仅作参考 , 没有任何培训广告 。 笔者 只负责搜集整理 ,对于 各证书含金量不做评价 。证书排序 按照收集的顺序 , 没有任何排名 。 内容会 尽量保持持续更新 。由于能力有限,难免出现各种错误

    2024年02月04日
    浏览(40)
  • 嵌入式相关开源项目、库、资料------持续更新中

    学习初期最难找的就是找学习资料了,本贴精心汇总了一些嵌入式相关资源,包括但不限于编程语言、单片机、开源项目、物联网、操作系统、Linux、计算机等资源,并且在不断地更新中,致力于打造全网最全的嵌入式资料库。有好的嵌入式相关资源的朋友欢迎做贡献,利人

    2024年02月02日
    浏览(29)
  • 神经网络数据增强transforms的相关操作(持续更新)

    日志:         2022.6.18:加入transforms.TenCrop() (1)transforms.ToTensor() 可将PIL格式、数组格式转换为tensor格式 (2)transforms.ToPILImage() 将 tensor格式 或者 数组类型 的数据转换为 PIL 格式 (3)transforms.Normalize() 注意该操作只能对tensor格式进行操作,因此放transforms.ToTensor()之后

    2023年04月08日
    浏览(20)
  • 电气领域相关数据(目标检测,分类图像数据及负荷预测,持续更新)

    可下载版,持续更新 1. 电力设备红外图像与可见光图像配准数据集(103对图像,绝缘套管)    下载地址:电力设备红外图像与可见光图像配准数据集(103对图像) 2.变电站红外图像数据集(电压电流互感器,VOC标签,889张) 下载地址: 变电站红外图像数据集(电压电流

    2024年02月07日
    浏览(34)
  • 算法面试-深度学习基础面试题整理-AIGC相关(2023.9.01开始,持续更新...)

    1、stable diffusion和GAN哪个好?为什么 ? Stable diffusion是一种基于随机微分方程的生成方法,它通过逐步增加噪声来扰动原始图像,直到完全随机化。然后,它通过逐步减少噪声来恢复图像,同时使用一个神经网络来预测下一步的噪声分布。Stable Diffusion的优点是可以在连续的潜

    2024年02月10日
    浏览(32)
  • CTF-XXE(持续更新,欢迎分享更多相关知识点的题目)

    进来看到 然后一起看 Write 进来看到 一起看 write 反正是XXE 直接整 write 不整花里胡哨,解题在最下面 write 与博主不同,我通过下面的语句得到了三个地址,其中两个通过c段扫描可以直接出来flag。 flag出来了,输入平台却不对

    2024年02月11日
    浏览(26)
  • 【HDFS】与单测编写相关的一些工具类及方法(大纲篇)持续更新

    MiniDFSCluster 可以用这个类创建一个单进程的DFS集群用来进行单元测试。 一般是采用MiniDFSCluster$Builder去建造出一个MiniDFSCluster对象。builder可以指定很多参数 获取cluster里的某个DataNode对象 【HDFS】单测中MiniDFSCluster获取某个DataNode对象 MiniRouterDFSCluster 用来模拟一个有多台Router的

    2024年02月15日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包