leetcode

solve questions in leetcode by Rust

Stars
366
Committers
1

leetcode

Solve questions in leetcode by Rust

前言

由于 Rust 写数据结构相关的资料特别少并且理解非常困难,所以专门建了个 Repo 用来记录 Rust 刷 leetcode 的解法并包含心得体会,欢迎 Star✨ 会长期稳定更新。 努力写出最容易理解的 Rust 代码。 https://github.com/zhangyuang/leetcode

注: 以下代码并没有刻意追求最优解,主要目的在于熟悉 Rust 语法以及使用可读性强便于理解的代码来解决问题。欢迎 Star✨ 长期稳定保持更新。

相关资料

官方 API 文档 Rust 程序设计语言中文版

debug in VSCode

建议本地编码时使用 VSCode 自带的 lldb 调试功能来进行断点调试,提升开发效率

// setting.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "lldb",
      "request": "launch",
      "name": "Debug",
      "args": [],
      "cwd": "${workspaceFolder}",
      "cargo": {
        "args": [
          "test",
          "--manifest-path=.test_repo/Cargo.toml"
        ],
        "filter": {
          "name": "leetcodebyrust",
          "kind": "lib"
        }
      },
    }
  ]
}

F5/Fn+F5 启动调试

lldb 调试 Rust

我们通过 lldb 来调试 Rust 代码,同样我们会经常需要在 Debug Console 中打印出当前的一些变量值。这里需要特殊配置,根据 VScode LLDB 文档描述 Debug Console 提供两种执行模式。分别是以 lldb commands 模式执行,或者 expressions 表达式的形式执行。

当我们需要进行表达式求值时需要在前面加上 ? 符号。例如 ?foo 打印 foo 的值。

也可以通过 settings.json 中配置 "lldb.consoleMode": "evaluate" 默认启用 evaluate 模式,不再需要输入 ? 符号。此时调用 lldb commands 需要添加 /cmd 开头

分类

链表(linkedList) 二叉树(binaryTree) 动态规划(dynamic programing) HOT100🔥

linkedList

链表

Rust 解链表题思路

Go 程序员已经下班 Cpp 程序员还在打断点 Rust 程序员还在编译

Rust 解决数据结构问题相比于其他语言十分的困难,就在于变量所有权的 move(转移)与 borrow(借用)。

遍历链表

通常使用可变引用来遍历, 注意这里需要对 Option<Box<ListNode>> struct 使用 as_ref 或者 as_mut 来进行引用遍历。根据官方文档的解释,我们可以看出 as_ref/as_mut 在这里的作用是 Converts from &Option<T> to Option<&T>


let mut root = &mut head;
while let Some(node) = root {
  let next_node = &mut node.next;
  // 使用as_mut获取next_node的引用,使用&mut获取.next的引用。以此来获取root下一个节点的下一个节点的引用。直接使用unwrap会导致所有权的move
  let next_node_next = &mut next_node.as_mut()?.next;
  // 这里面不能再直接使用head,因为head的所有权已经借给了root,在循环体中未归还
  // other code...
  root = &mut node.next;
}

写法二

while head.as_mut()?.next.is_some() {
    head = &mut head.as_mut()?.next;
}
转移获取链表节点所有权
  • take 方法使用方式见文档
  • Copy 以及 Clone 的区别可查看该文章

// 因为next为Box智能指针存储在堆上的节点,不具备Copy属性,无法直接从堆上转移数据否则会造成多次释放的问题。使用take方法将所有权转移出去,并且在原位置留下了None。
let next_node = node.next.take();

Rust 解决 树 题思路

共享树节点

这里我们尽量不使用 clone 或者 take 来重复获取树节点的所有权,这样会导致性能低下以及影响入参树的数据结构, 这里我们使用 Rc::clone

let root_borrow = root.as_ref().unwrap().borrow();
let left = if root_borrow.left.is_some() {
    Some(Rc::clone(root_borrow.left.as_ref().unwrap()))
} else {
    None
};
let right = if root_borrow.right.is_some() {
    Some(Rc::clone(root_borrow.right.as_ref().unwrap()))
} else {
    None
};

解题代码

皆通过 leetcode 测试用例,可直接粘贴到 leetcode 编辑器中调试,刷题建议由浅入深,按知识点来刷,不要左右横跳。

Easy

简单难度的链表题

回文链表|is_palindrome 反转链表|reverse_list 链表的中间节点|middle_node 删除链表节点|delete_node 删除链表重复节点|delete_duplicates

Medium

中等难度的链表题

两数相加|add_two_numbers 两两交换链表中的节点|swap_pairs 删除链表的倒数第 N 个节点|remove_nth_from_end 合并两个链表|merge_in_between 旋转链表|rotate_right 从链表中删去总和值为零的连续节点|remove_zero_sum_sublists 链表中的下一个更大节点|next_larger_nodes 删除链表重复节点 2|delete_duplicate

Tree

树,二叉树

解题思路

Rust 构造树需要使用 Rc引用计数智能指针以及 RefCell,使得一个数据具有多个可变的所有者。因为一个子节点可能被多个父节点所共享。

Easy

简单难度的树题 二叉搜索树解题思路:中序遍历的结果是递增数组

二叉树的层次遍历 II|level_order_bottom 二叉树的层平均值|average_of_levels 相同的树|is_symmetric 对称二叉树|is_same_tree 平衡二叉树|is_balanced 二叉树的所有路径|binary_tree_paths 二叉树的最小深度|min_depth 左叶子之和|sum_of_left_leaves 二叉搜索树中的众数|find_mode 二叉搜索树中的搜索|search_bst 二叉搜索树的第 k 大节点|kth_largest 二叉搜索树的范围和|range_sum_bst 二叉搜索树节点最小距离|min_diff_in_bst 把二叉搜索树转换为累加树|convert_bst 将有序数组转换为二叉搜索树|sorted_array_to_bst 另一个树的子树|is_subtree 叶子相似的树|leaf_similar 563 二叉树的坡度

Medium

中等难度的树题

二叉树前序遍历|preorder_traversal 二叉树中序遍历|inorder_traversal 二叉树层次遍历|level_order 二叉树展开为链表|flatten 不同的二叉搜索树|num_trees 验证二叉搜索树|is_valid_bst 二叉树的锯齿形层次遍历|zigzag_level_order 最长同值路径|longest_univalue_path 前缀树|Trie 从前序与中序遍历序列构造二叉树|build_tree 根据前序和后序遍历构造二叉树|construct_from_pre_post 最大二叉树|construct_maximum_binary_tree 完全二叉树的节点个数|count_nodes

Hard

二叉树后序遍历|postorder_traversal

DynamicPrograming

动态规划

Rust 解动态规划题思路

主要思路与其他语言类似。还是通过寻找状态转移方程(递推关系),通常要使用 vec 来保存之前的结果来提升性能。 常用到的空间优化方式有滚动数组,来将二维数组压成一维或减少数组空间大小。大部分情况都是背包问题(01背包,完全背包,多重背包)问题的变种。 学习资料: liweiwei leetcode 经典动规解析

Easy

简单难度的动态规划题

爬楼梯|climb_stairs 三步问题|ways_to_step 连续数列|max_sub_array 按摩师|massage 打家劫舍|rob 使用最小花费爬楼梯|min_cost_climbing_stairs 买卖股票的最佳时机|max_profit 最长连续递增序列|find_length_of_lcis 区域和检索 - 数组不可变|NumArray 有序数组的平方|sorted_squares 509 斐波那契数

Medium

中等难度的动态规划题

最长上升子序列|length_of_lis 最长递增子序列的个数|find_number_of_lis 最小路径和|min_path_sum 最长回文子串|longest_palindrome 打家劫舍 II|robs 打家劫舍 III|robs 不同路径 II|unique_paths_with_obstacles 二维区域和检索 - 矩阵不可变|NumMatrix 完全平方数|num_squares 55跳跃游戏 45跳跃游戏|| 413等差数列划分 221最大正方形

HOT100🔥

Hot100类型题

Easy

简单难度的HOT100题

柠檬水找零|lemonade_change 找到所有数组中消失的数字|find_disappeared_numbers 最短无序连续子数组|find_unsorted_subarray 字符串相加|add_strings 二分查找|binary_search 第一个错误的版本|first_bad_version

Medium

中等难度的HOT100题

除自身以外数组的乘积|product_except_self 分割等和子集|can_partition 全排列|permute 括号生成|generate_parenthesis 子集|subsets 零钱兑换|coin_change 不同路径|unique_paths 单词搜索|exist 单词拆分|word_break 无重复字符的最长子串|length_of_longest_substring 课程表|can_finish 在排序数组中查找元素的第一个和最后一个位置|search_range

Hard

困难难度的Hot100题目

接雨水|trap

Others

其他分类的题目集合

Easy

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面|exchange 用栈实现队列|MyQueue 用队列实现栈|MyStack 最小栈|MinStack 用栈操作构建数组|build_array 判断子序列|is_subsequence 821 字符的最短距离 997 找到小镇的法官 118 杨辉三角

Medium

807 保持城市天际线 11 盛最多水的容器 475 供暖器 390 消除游戏 334 递增的三元子序列 373 查找和最小的K对数字

Hard

缺失的第一个正数|first_missing_positive

周赛

记录周赛题目

2020.8.9 双周赛

第 k 个缺失的正整数|find_kth_positive K 次操作转变字符串|can_convert_string