当前位置: 首页 > news >正文

【Rust】枚举类型创建单链表以及常见的链表操作方法

目录

单链表

用枚举表达链表

枚举enum

Box容器

创建节点

1. 创建并打印

2. match 匹配

3. 节点初始化

4.节点嵌套

追加节点

1. 尾插法

2. 链表追加方法

3. 头插法

4. 改写成单链表方法

遍历链表

1. 递归法

2. 递推法

3. 改写成单链表方法 

自定义Display trait

创建链表

1. 递归法

2. 递推法

3. 改写成单链表方法 

链表长度

翻转链表

1. 递归法

2. 递推法

3. 改写成单链表关联函数和方法

删除尾节点

汇总小结

相关方法

自定义trait

完整代码

真题实战

合并两个有序链表 Mmerge-two-sorted-lists


20210817204340750.png

单链表

单链表(Linked List)是一种线性数据结构,由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。单链表的特点是每个节点只能指向一个下一个节点,没有指向上一个节点的指针。

单链表的基本操作包括插入、删除、查找等。插入操作需要在指定位置插入一个新节点,删除操作需要删除指定位置的节点,查找操作需要找到指定值的节点位置。

单链表的优势是插入和删除操作相对简单,不需要移动大量元素,时间复杂度为O(1)。但单链表在查找操作上的时间复杂度是O(n),因为需要从头节点开始逐个比较直到找到目标节点。

用枚举表达链表

enum List {None,Node(i32, Box<List>),
}

枚举成员: List::None 、 List::Node(i32, Box<List>)

枚举enum

Rust 是一种编程语言,支持多种类型的的数据,其中之一就是枚举类型(Enum)。枚举类型是一种特殊的数据类型,它定义了一组可能的值。每个值都有一个特定的类型,并且可以用来表示不同的含义。

Rust 中的枚举类型通常使用关键字 enum 来定义,后接名称;枚举有多个成员(variant),每个成员是该 enum 类型的一个可能值。

例如,定义一个表示颜色类型的的枚举:

enum Color { Red, Green, Blue, }

这个 Color 类型有三种可能的值:RedGreen 和 Blue,使用这些值来存储不同颜色的值。

枚举类型的一个重要作用是使得代码更具可读性和可维护性。例如,如果使用一个 String 类型的颜色值,必须要确保该字符串始终有效,否则程序可能会崩溃。而使用枚举类型,可以明确指定可能的值,从而减少了此类错误的发生。

另外,Rust 还支持模式匹配,可以方便地检查一个枚举类型的值是哪个模式,这使得代码更加简洁和可读。例如:

let c = Color::Red;  
match c {  Color::Red => println!("It's red!"),  Color::Green => println!("It's green!"),  Color::Blue => println!("It's blue!"),  
};

在这个例子中,使用 match 表达式来检查 c 的值是哪个模式。根据不同的模式,可以执行不同的代码。

Box容器

Rust中的Box是一种通用容器,可以存储任意类型的数据。它类似于C++中的unique_ptr,但还提供了一些额外的功能。

通过使用Box,您可以在堆上分配内存,而不需要手动管理内存的分配和释放。这使得使用Box成为一种方便的方式来存储和管理动态分配的内存。

Box还提供了一些优势,例如能够解决递归类型的大小问题。在Rust中,基本类型无法实现递归,但可以使用Box将递归类型的大小固定下来。

以下是一个使用Box的简单示例:

use std::boxed::Box;  fn main() {  let ibox = Box::new(5);  println!("{}", ibox); println!("{}", *ibox);println!("{}", ibox.as_ref());
}

这个程序创建了一个Box容器,输出结果为3个5,但有不同的含义:

println!("{}", ibox);
这行代码使用 println! 宏打印一个 Box 对象 ibox 的值。{} 是一个占位符,表示将对象的值插入到此处。在这种情况下,ibox 的值将被打印出来,因为 Box 类型实现了 Display trait,可以将其值转换为字符串形式。

println!("{}", *ibox);
这行代码使用 println! 宏打印 ibox 解引用后的值。在 Rust 中,* 运算符用于解引用指针,即获取指针指向的实际值。在这种情况下,ibox 是一个 Box 对象,存储了一个整数值。通过解引用操作,我们获取到这个整数值并打印出来。

println!("{}", ibox.as_ref());
这行代码使用 println! 宏打印 ibox 的引用。as_ref() 方法返回一个引用,可以用于访问 Box 对象中的数据。在这种情况下,我们将 ibox 转换为引用,并通过引用访问其值并打印出来。

通过使用Box,可以方便地在Rust中管理和访问动态分配的内存,而不需要手动管理内存的分配和释放。

创建节点

1. 创建并打印

创建单个节点,并使用 #[derive(Debug)] 及 println! 打印输出:

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn main() {let head = List::Node(1, Box::new(List::None));println!("{:?}", head);let head = List::Node(2, Box::new(List::None));println!("{:?}", head);
}

输出: 

Node(1, None)
Node(2, None)

2. match 匹配

创建节点,并使用match表达式匹配枚举成员:

enum List {None,Node(i32, Box<List>),
}fn main() {let list1 = List::None;let list2 = List::Node(1, Box::new(List::None));match list1 {List::None => println!("List::None"),List::Node(_, _) => println!("List::Node"),}match list2 {List::None => println!("List::None"),List::Node(value, _) => println!("List::Node({})", value),}
}

输出:

List::None
List::Node(1)

3. 节点初始化

new()初始化函数:fn new(value: i32)

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}impl List {fn new(value: i32) -> Self {List::Node(value, Box::new(List::None))}
}fn main() {let head = List::new(1);println!("{:?}", head);let head = List::new(2);println!("{:?}", head); let head = List::new(3);println!("{:?}", head); 
}

输出: 

Node(1, None)
Node(2, None)
Node(3, None)

4.节点嵌套

通过嵌套多个节点形成单链表:

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn main() {let head = List::Node(1, Box::new(List::Node(2, Box::new(List::Node(3, Box::new(List::Node(4, Box::new(List::None),)),)),)),);println!("{:?}", head);
}

输出: 

Node(1, Node(2, Node(3, Node(4, None)))) 


通过枚举类型表达成一个单链表,同样,这种方法甚至还能表达一棵二叉树

#[derive(Debug)]
enum Tree {  None,  Node(i32, Box<Tree>, Box<Tree>),  
} fn main() { let tree = Tree::Node(  1,  Box::new(Tree::Node(2, Box::new(Tree::None), Box::new(Tree::None))),  Box::new(Tree::Node(3,Box::new(Tree::Node(4, Box::new(Tree::None), Box::new(Tree::None))),Box::new(Tree::Node(5, Box::new(Tree::None), Box::new(Tree::None))),),),  );   println!("{:?}", tree);
}

输出: 

Node(1, Node(2, None, None), Node(3, Node(4, None, None), Node(5, None, None)))

二叉树相对单链表要复杂,有空再研究;还是继续探究单链表的其它操作吧。


追加节点

1. 尾插法

函数 append() 在链表尾部追加节点:

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn append(list: &mut List, value: i32) {match list {List::None => {*list = List::Node(value, Box::new(List::None));}List::Node(_, next) => {append(next, value);}}
}fn main() {let mut head = List::Node(1, Box::new(List::Node(2, Box::new(List::Node(3, Box::new(List::Node(4, Box::new(List::None),)),)),)),);println!("{:?}", head);append(&mut head, 5);println!("{:?}", head);
}

输出: 

Node(1, Node(2, Node(3, Node(4, None))))
Node(1, Node(2, Node(3, Node(4, Node(5, None)))))

2. 链表追加方法

把append()函数改造成链表方法:

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}impl List {fn new(value: i32) -> Self {List::Node(value, Box::new(List::None))}fn append(self, value: i32) -> Self {match self {List::None => List::Node(value, Box::new(List::None)),List::Node(v, next) => List::Node(v, Box::new(next.append(value))),}}
}fn main() {let head = List::new(1);println!("{:?}", head);let head = head.append(2);println!("{:?}", head);let head = head.append(3);println!("{:?}", head);
}

输出: 

Node(1, None)
Node(1, Node(2, None))
Node(1, Node(2, Node(3, None))) 

3. 头插法

除了尾部追加,也能在头部插入: 

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn main() {let mut head = List::Node(1, Box::new(List::None));head = List::Node(2, Box::new(head));head = List::Node(3, Box::new(head));head = List::Node(4, Box::new(head));println!("{:?}", head);
}

输出: 

Node(4, Node(3, Node(2, Node(1, None)))) 

头插法得到一个反序的单链表:

4. 改写成单链表方法

对比append()和.push_back()不同之处:

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}impl List {fn new(value: Option<i32>) -> Self {match value {Some(value) => List::Node(value, Box::new(List::None)),None => List::None,}}fn append(self, value: i32) -> Self {match self {List::None => List::new(Some(value)),List::Node(v, next) => List::Node(v, Box::new(next.append(value))),}}fn push_back(&mut self, value: i32) {match self {List::None => {*self = List::new(Some(value));}List::Node(_, next) => {next.push_back(value);}}}
}fn main() {let head = List::new(None);println!("{:?}", head);let head = head.append(1);let head = head.append(2);println!("{:?}", head);println!();let mut head = List::new(Some(1));println!("{:?}", head);for value in 2..5 {head.push_back(value);}println!("{:?}", head);
}

输出: 

None
Node(1, Node(2, None))

Node(1, None)
Node(1, Node(2, Node(3, Node(4, None))))

push_back()推荐使用递归法,代码比较精炼;递推迭代法如下:

    fn push_back(&mut self, value: i32) {match self {List::None => {*self = List::new(Some(value));}List::Node(_, next) => {if let List::None = &**next {*next = Box::new(List::new(Some(value)));} else {let mut curr = next;while let List::Node(_, ref mut next) = **curr {curr = next;}*curr = Box::new(List::new(Some(value)));}}}}

遍历链表

1. 递归法

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn traverse(list: &List) {match list {List::None => println!("nil"),List::Node(value, next) => {print!("{}->", value);traverse(next);}}
}fn main() {let head = List::Node(1,Box::new(List::Node(2,Box::new(List::Node(3,Box::new(List::None),)),)),);println!("{:?}", head);traverse(&head);
}

输出:

Node(1, Node(2, Node(3, None)))
1->2->3->nil

2. 递推法

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn main() {let head = List::Node(1,Box::new(List::Node(2,Box::new(List::Node(3,Box::new(List::None),)),)),);println!("{:?}", head);traverse(&head);
}

输出:

Node(1, Node(2, Node(3, None)))
1->2->3->nil

while let 语句比较简洁,相当于loop+if let:

fn traverse(head: &List) {let mut cur = head;loop {if let List::Node(value, next) = cur {print!("{}->", value);cur = next;} else {println!("nil");break;}}
}

使用 loop+match 也能遍历:

fn traverse(head: &List) {let mut cur = head;loop {match cur {List::None => {println!("nil");break;}List::Node(value, next) => {print!("{}->", value);cur = next;}}}
}

3. 改写成单链表方法 

enum List {None,Node(i32, Box<List>),
}impl List {fn traverse(&self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}
}fn main() {let head = List::Node(1, Box::new(List::Node(2, Box::new(List::Node(3, Box::new(List::None),)),)),);head.traverse();
}

输出:

1->2->3->nil


自定义Display trait

为与遍历函数区分,在trait输出时用“Nil”标记

use std::fmt;#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}impl fmt::Display for List {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {List::None => write!(f, "Nil"),List::Node(value, next) => {write!(f, "{}->", value)?;write!(f, "{}", *next)?;Ok(())}}}
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn main() {  let head = List::Node(1, Box::new(List::Node(2, Box::new(List::Node(3, Box::new(List::None),)),)),);println!("{}", head);traverse(&head);
}

输出:

1->2->3->Nil
1->2->3->nil


创建链表

直接把动态数组Vec创建成单链表。

1. 递归法

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn create(values: Vec<i32>) -> List {fn _helper(values: &Vec<i32>, index: usize) -> List {if index < values.len() {let value = values[index];let next = Box::new(_helper(values, index + 1));List::Node(value, next)} else {List::None}}_helper(&values, 0)
}fn main() {let values = vec![1, 2, 3, 4, 5];let head = create(values.clone());println!("{:?}", head);traverse(&head);
}

输出:

Node(1, Node(2, Node(3, Node(4, Node(5, None)))))
1->2->3->4->5->nil

2. 递推法

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list
}fn main() {let values = vec![1, 2, 3, 4, 5];let head = create(values.clone());println!("{:?}", head);traverse(&head);
}

输出:

Node(1, Node(2, Node(3, Node(4, Node(5, None)))))
1->2->3->4->5->nil

3. 改写成单链表方法 

enum List {None,Node(i32, Box<List>),
}impl List {fn traverse(&self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list}
}fn main() {let head = List::create(vec!(1,2,3,4,5));head.traverse();
}

输出:

1->2->3->4->5->nil


链表长度

遍历链表时改打印为计数即可:

enum List {None,Node(i32, Box<List>),
}impl List {fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list}fn traverse(&self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}fn get_size(&self) -> usize {let mut length = 0;let mut curr = self;while let List::Node(_, next) = curr {length += 1;curr = next;}length}
}fn main() {let head = List::create(vec![1, 2, 3]);head.traverse();let length = head.get_size();println!("Length of list: {}", length);let head = List::create(vec![1, 2, 3, 4, 5]);head.traverse();let length = head.get_size();println!("Length of list: {}", length);
}

输出:

1->2->3->nil
Length of list: 3
1->2->3->4->5->nil
Length of list: 5


翻转链表

把单链表翻转,即头尾反向。

1. 递归法

#[derive(Debug)]
enum List {None,Node(i32, Box<List>),
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list
}fn reversed(list: &List) -> List {fn _helper(list: &List, prev: List) -> List {match list {List::None => prev,List::Node(value, next) => {_helper(next, List::Node(*value, Box::new(prev)))},}}_helper(list, List::None)
}fn main() {let values = vec![1, 2, 3, 4, 5];let head = create(values);traverse(&head);let head = reversed(&head);traverse(&head);
}

输出:

1->2->3->4->5->nil
5->4->3->2->1->nil

2. 递推法

enum List {None,Node(i32, Box<List>),
}fn traverse(head: &List) {let mut curr = head;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");
}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list
}fn reversed(head: &List) -> List {let mut list = List::None;let mut curr = head;while let List::Node(value, next) = curr {list = List::Node(*value, Box::new(list));curr = next;}list
}fn main() {let values = vec![1, 2, 3, 4, 5];let head = create(values);traverse(&head);let head = reversed(&head);traverse(&head);
}

输出:

1->2->3->4->5->nil
5->4->3->2->1->nil

3. 改写成单链表关联函数和方法

对比关联函数reversed()和方法.reverse()不同之处:

enum List {None,Node(i32, Box<List>),
}impl List {fn new(value: Option<i32>) -> Self {match value {Some(value) => List::Node(value, Box::new(List::None)),None => List::None,}}fn traverse(&self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list}fn reversed(&self) -> Self {let mut list = List::new(None);let mut curr = self;while let List::Node(value, next) = curr {list = List::Node(*value, Box::new(list));curr = next;}list}fn reverse(&mut self) {let mut tail = List::new(None);let mut curr = std::mem::replace(self, List::None);while let List::Node(value, next) = std::mem::replace(&mut curr, List::None) {let node = List::Node(value, Box::new(tail));(tail, curr) = (node, *next);}std::mem::swap(self, &mut tail);}
}fn main() {let values = vec![1, 2, 3, 4, 5];let mut head = List::create(values);head.traverse();head.reverse();head.traverse();head = List::reversed(&head);head.traverse();
}

输出:

1->2->3->4->5->nil
5->4->3->2->1->nil
1->2->3->4->5->nil


删除尾节点

删除链表尾节点pop(),并返回尾节点的值

#![allow(dead_code)]enum List {None,Node(i32, Box<List>),
}impl List {fn new(value: Option<i32>) -> Self {match value {Some(value) => List::Node(value, Box::new(List::None)),None => List::None,}}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list}fn traverse(&self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}fn pop(&mut self) -> Option<i32> {match self {List::None => None,List::Node(value, next) => {if let List::None = **next {let popped_value = *value;*self = List::None;Some(popped_value)} else {next.pop()}}}}
}fn main() {let values = vec![1, 2, 3, 4, 5];let mut head = List::create(values);head.traverse();for _ in 1..=6 {if let Some(value) = head.pop() {println!("Popped value: {}", value);} else {println!("List is empty");}head.traverse();}
}

输出:

1->2->3->4->5->nil
Popped value: 5
1->2->3->4->nil
Popped value: 4
1->2->3->nil
Popped value: 3
1->2->nil
Popped value: 2
1->nil
Popped value: 1
nil
List is empty
nil


汇总小结

List 枚举定义了链表的两种可能状态:None 和 Node。

其中,Node 表示链表节点,包含一个整数值和一个指向下一个节点的 Box<List>。

相关方法

相关的方法或关联函数,归纳如下:

new:创建一个新的链表节点或空链表。
append:在链表尾部添加一个节点,并返回添加后的链表。
push_back:在链表尾部添加一个节点,将链表修改为添加后的结果。
create:根据给定的数组构建一个链表。
reversed:反转链表,返回一个反转后的链表。
reverse:反转链表,将链表修改为反转后的结果。
traverse:遍历并打印链表中的节点值。
get_size:遍历出链表的节点数,返回链表长度。
pop:删除链表尾节点,并返回尾节点的值。

自定义trait

自定义trait Display,使链表的输出不依赖trait Debug:

```Rust
impl std::fmt::Display for List {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            List::None => write!(f, "nil"),
            List::Node(value, next) => {
                write!(f, "{}->", value)?;
                write!(f, "{}", *next)?;
                write!(f, "")
            }
        }
    }
}
```

完整代码

InsCode - 让你的灵感立刻落地

https://inscode.csdn.net/@boysoft2002/RustEnumList

#![allow(dead_code)]enum List {None,Node(i32, Box<List>),
}impl std::fmt::Display for List {fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {match self {List::None => write!(f, "nil"),List::Node(value, next) => {write!(f, "{}->", value)?;write!(f, "{}", *next)?;Ok(())}}}
}impl List {fn new(value: Option<i32>) -> Self {match value {Some(value) => List::Node(value, Box::new(List::None)),None => List::None,}}fn append(self, value: i32) -> Self {match self {List::None => List::new(Some(value)),List::Node(v, next) => List::Node(v, Box::new(next.append(value))),}}fn push_back(&mut self, value: i32) {match self {List::None => {*self = List::new(Some(value));}List::Node(_, next) => {next.push_back(value);}}}fn create(values: Vec<i32>) -> Self {let mut list = List::new(None);for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list}fn get_size(&self) -> usize {let mut length = 0;let mut curr = self;while let List::Node(_, next) = curr {length += 1;curr = next;}length}fn reversed(&self) -> Self {let mut list = List::new(None);let mut curr = self;while let List::Node(value, next) = curr {list = List::Node(*value, Box::new(list));curr = next;}list}fn reverse(&mut self) {let mut tail = List::new(None);let mut curr = std::mem::replace(self, List::None);while let List::Node(value, next) = std::mem::replace(&mut curr, List::None) {let node = List::Node(value, Box::new(tail));(tail, curr) = (node, *next);}std::mem::swap(self, &mut tail);}fn pop(&mut self) -> Option<i32> {match self {List::None => None,List::Node(value, next) => {if let List::None = **next {let popped_value = *value;*self = List::None;Some(popped_value)} else {next.pop()}}}}fn traverse(self: &Self) {let mut curr = self;while let List::Node(value, next) = curr {print!("{}->", value);curr = next;}println!("nil");}
}  fn main() {  let head = List::new(None);head.traverse();let head = head.append(1);let head = head.append(2);head.traverse();println!();let mut head = List::new(Some(1));head.traverse();for value in 2..5 {head.push_back(value);}head.traverse();println!();let values = vec![1, 2, 3, 4, 5];head = List::create(values);head.traverse();head = head.reversed();println!("{}", head);head.reverse();println!("{}", head);let length = head.get_size();println!("Length of list: {}", length);println!();if let Some(value) = head.pop() {println!("Popped value: {}", value);} else {println!("List is empty");}head.traverse();println!("Length of list: {}", head.get_size());
}

输出:

nil
1->2->nil

1->nil
1->2->3->4->nil

1->2->3->4->5->nil
5->4->3->2->1->nil
1->2->3->4->5->nil
Length of list: 5

Popped value: 5
1->2->3->4->nil
Length of list: 4


真题实战

合并两个有序链表 Mmerge-two-sorted-lists

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例 1:

d661704fd7a9313cfefde8cc55ef4666.jpg

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

代码:

#[derive(Clone)]
enum List {None,Node(i32, Box<List>),
}fn create(values: Vec<i32>) -> List {let mut list = List::None;for &value in values.iter().rev() {list = List::Node(value, Box::new(list));}list
}fn traverse(head: &List) {let mut cur = head;while let List::Node(value, next) = cur {print!("{}->", value);cur = next;}println!("nil");
}fn merge_lists(l1: List, l2: List) -> List {match (l1.clone(), l2.clone()) {(List::None, _) => l2,(_, List::None) => l1,(List::Node(x, box1), List::Node(y, box2)) => {if x < y {List::Node(x, Box::new(merge_lists(*box1, l2)))} else {List::Node(y, Box::new(merge_lists(l1, *box2)))}}}
}fn main() {let nums1 = vec![1, 2, 4];let nums2 = vec![1, 2, 3];let list1 = create(nums1);let list2 = create(nums2);traverse(&list1);traverse(&list2);let merged = merge_lists(list1, list2);traverse(&merged);
}

输出:

1->2->4->nil
1->2->3->nil
1->1->2->2->3->4->nil

相关文章:

【Rust】枚举类型创建单链表以及常见的链表操作方法

目录 单链表 用枚举表达链表 枚举enum Box容器 创建节点 1. 创建并打印 2. match 匹配 3. 节点初始化 4.节点嵌套 追加节点 1. 尾插法 2. 链表追加方法 3. 头插法 4. 改写成单链表方法 遍历链表 1. 递归法 2. 递推法 3. 改写成单链表方法 自定义Display tr…...

Excel 两列数据中相同的数据进行同行显示

一、要求 假设您有两个列&#xff0c;分别是A列和B列&#xff0c;需要在C列中找出A列对应的B列的值。 二、方案 方法1&#xff1a;寻常思路 凸显重复项对A列单独进行筛选–按颜色进行排序&#xff0c;然后升序对B列重复上述操作即可 方法2&#xff1a;两个公式 VLOOKUP 纵向查找…...

Windows本地安装配置Qcadoo MES系统

简介 Qcadoo MES是一款功能强大且灵活的开源MES&#xff08;制造执行系统&#xff09;&#xff0c;旨在为制造业务提供全面的管理和监控解决方案。本篇博客将教您如何在Windows操作系统上安装和配置Qcadoo MES系统&#xff0c;以便您能够轻松管理和监控制造过程。 环境要求 …...

涛思数据与拾贝云达成战略合作,携手赋能工业数字化转型

2023 年 7 月 27 日&#xff0c;北京涛思数据科技有限公司&#xff08;以下简称“涛思数据”&#xff09;与广州拾贝云科技有限公司&#xff08;以下简称“拾贝云”&#xff09;于广州签署战略合作协议。双方围绕电力行业的需求与痛点展开积极讨论&#xff0c;就如何量身打造最…...

nginx 配置多域名多站点 Ubuntu

nginx 配置多域名多站点 Ubuntu 一、安装 nginx apt install nginx二、配置文件说明 nginx 的配置文件在 /etc/nginx 目录下&#xff0c;它的默认内容是这样的 root2bd0:/etc/nginx# ll total 72 drwxr-xr-x 8 root root 4096 Jul 31 15:21 ./ drwxr-xr-x 104 root root …...

Docker实践:使用Docker搭建个人开发环境(极简版)

文章目录 说明教程1. 编写 Dockerfile2. 编写 docker-compose.yml3. 使用容器创建容器启动容器进入容器命令行VSCode 4. 关闭容器5. 备份容器导出导入 6. 重置容器 相关资料文章合集详细了解本文在个人电脑上安装 Docker容器使用 NVIDIA 显卡托管镜像运行GUI程序 说明 本文是在…...

SQL从三个表中根据时间分别查询并汇总数量一行展示

需求&#xff1a;如果您要从三个表中根据时间分别查询并汇总数量&#xff0c;然后将结果以时间和数量一行展示&#xff0c;可以使用子查询和条件聚合。 入库主表 入库明细表 出库主表 出库明细表 退货主表 退货明细表 SQL代码 SELECT time,sum(a.inQty) as inQty,sum(a.outQty…...

同样是跨端框架,React会不会被VUE取代?

看到知乎上有比较多的类似问题&#xff0c;正好这两个框架在以往的一些项目中都有实践过&#xff0c;就借着本篇文章说说我个人的看法。 先摆个结论&#xff1a;不会&#xff0c;毕竟各有千秋&#xff0c;除非跨端框架有被更好的概念所替代&#xff0c;又或者App已经彻底过气了…...

Excel·VBA定量装箱、凑数值金额、组合求和问题

如图&#xff1a;对图中A-C列数据&#xff0c;根据C列数量按照一定的取值范围&#xff0c;组成一个分组装箱&#xff0c;要求如下&#xff1a; 1&#xff0c;每箱数量最好凑足50&#xff0c;否则为47-56之间&#xff1b; 2&#xff0c;图中每行数据不得拆分&#xff1b; 3&…...

通过Jmeter压测存储过程

目录 一、存储过程准备&#xff1a; 二、测试工具准备&#xff1a; 三、工具配置及执行&#xff1a; 1、配置JDBC Connection Configuration&#xff1a; 2、配置吞吐量控制器&#xff08;可跳过&#xff09;&#xff1a; 3、配置JDBC Request&#xff1a; 对于存储过程…...

Spring笔记之Spring对IoC的实现

文章目录 IoC控制反转依赖注入set注入注入外部Bean注入内部Bean注入简单类型通过注入方式实现javax.sql.DateSource接口测试简单类型 级联属性赋值&#xff08;了解&#xff09;注入数组注入List集合注入Set集合注入Map集合注入Properties注入null和空字符串不给属性赋值使用 注…...

【eNSP】Telnet远程登录

Telnet远程登录 eNSP软件TelnetTelnet远程登录-路由连接关闭防火墙eNSP根据图1画图路线配置路由端口IP配置路由R1改名配置接口IP 配置路由R2 配置R2的远程登录设置登录用户授权级别退出登录超时时间 Telnet测试 eNSP软件 eNSP(Enterprise Network Simulation Platform)是一款由…...

SOP/详解*和**/python数据结构(iter,list,tuple,dict)/ 解包

一、错误解决合集 1. > combined_seq.named_children() 2. isinstance 2th parameter : must be a type or tuple of types > 改为tuple&#xff0c;不要用列表。改为 LLLayer (nn.Conv2d,nn.Linear) 3. File “test.py”, line 90, in calculate_fin_fout print(“hi”…...

使用webdriver-manager解决浏览器与驱动不匹配所带来自动化无法执行的问题

1、前言 在我们使用 Selenium 进行 UI 自动化测试时&#xff0c;常常会因为浏览器驱动与浏览器版本不匹配&#xff0c;而导致自动化测试无法执行&#xff0c;需要手动去下载对应的驱动版本&#xff0c;并替换原有的驱动&#xff0c;可能还会遇到跨操作系统进行测试的时候&…...

【vue】Vue中debugger报错 unexpected ‘debugger’ statement no-debugger

前言&#xff1a; Vue中debugger报错 unexpected ‘debugger’ statement no-debugger &#xff08;意外的“调试器”语句没有调试器&#xff09; eslink规则没有开启’debugger’ &#xff0c;被规则屏蔽了&#xff0c;需要手动放开 解决方法 方式一&#xff1a; 找到.esl…...

课题方向a

首先在无线感知的研究方向下,辅以深度学习和计算机视觉的技术和知识,可以从事哪些具体课题的研究?请你尽可能多的给出课题名称供我选择 在无线感知的研究方向下,辅以深度学习和计算机视觉的技术,有很多具体课题可以进行研究。以下是一些供您选择的课题名称: 基于深度学习…...

【Matter】基于Ubuntu 22.04 交叉编译chip-tool

编译工程之际&#xff0c;记录一下编译过程&#xff0c;免得后续遗忘&#xff0c;总结下来chip-tool 交叉编译涉及到的知识点&#xff1a; 需要了解如何支持交叉编译&#xff0c;基于GN编译框架需要理解应用库如何交叉编译&#xff0c;理解pkg-config的使用meson 编译&#xf…...

Qt/C++音视频开发50-不同ffmpeg版本之间的差异处理

一、前言 ffmpeg的版本众多&#xff0c;从2010年开始计算的项目的话&#xff0c;基本上还在使用的有ffmpeg2/3/4/5/6&#xff0c;最近几年版本彪的比较厉害&#xff0c;直接4/5/6&#xff0c;大版本之间接口有一些变化&#xff0c;特别是一些废弃接口被彻底删除了&#xff0c;…...

低碳 Web 实践指南

现状和问题 2023年7月6日&#xff0c;世界迎来有记录以来最热的一天。气候变化是如今人类面临的最大健康威胁。据世界卫生组织预测2030年至2050年期间&#xff0c;气候变化预计每年将造成约25万人死亡。这是人们可以真切感受到的变化&#xff0c;而背后的主要推手是碳排放。 …...

信息安全:网络安全体系 与 网络安全模型.

信息安全&#xff1a;网络安全体系 与 网络安全模型. 网络安全保障是一项复杂的系统工程&#xff0c;是安全策略、多种技术、管理方法和人员安全素质的综合。一般而言&#xff0c;网络安全体系是网络安全保障系统的最高层概念抽象&#xff0c;是由各种网络安全单元按照一定的规…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...