数据结构与算法-Rust 版读书笔记-2线性数据结构-栈

数据结构与算法-Rust 版读书笔记-2线性数据结构-栈

一、线性数据结构概念

数组、栈、队列、双端队列、链表这类数据结构都是保存数据的容器,数据项之间的顺序由添加或删除时的顺序决定,数据项一旦被添加,其相对于前后元素就会一直保持位置不变,诸如此类的数据结构被称为线性数据结构。

线性数据结构有两端,称为“左”和“右”,在某些情况下也称为“前”和“后”,当然也可以称为顶部和底部,名称不重要,重要的是这种命名展现出的位置关系表明了数据的组织方式是线性的。这种线性特性和内存紧密相关,因为内存就是一种线性硬件,由此也可以看出软件和硬件是如何关联在一起的。

线性数据结构说的并非数据的保存方式,而是数据的访问方式。

线性数据结构不一定代表数据项在内存中相邻。以链表为例,虽然其中的数据项可能在内存的各个位置,但访问是线性的。

区分不同线性数据结构的方法是查看它们添加和移除数据项的方式,特别是添加和移除数据项的位置。例如,一些数据结构只允许从一端添加数据项,另一些则允许从另一端移除数据项,还有的允许从两端操作数据项。这些变种及其组合形式产生了许多在计算机科学领域非常有用的数据结构,它们出现在各种算法中,用于执行各种实际且重要的任务。

1、栈:后进先出

栈是数据项的有序集合,其中,新项的添加和移除总发生在同一端,这一端称为顶部,与之相对的另一端称为底部。栈的底部很重要,因为栈中靠近底部的项是存储时间最长的,最近添加的项最先被移除。这种排序原则有时被称为后进先出(Last In First Out,LIFO)或先进后出(First In Last Out,FILO),所以较新的项靠近顶部,较旧的项靠近底部。

2、Rust 预备知识

1、trait

trait类似于Java中的接口,TS 的 interface,C++中的纯虚类,但却又不完全相同。

trait这个单词,本意为特征,在代码中的含义就是,让某个结构体拥有某个特征。

trait Shape {fn area(&self) -> f32{return 0.0; } //该函数是实现可写可不写,如果不写,那么实现该Trait的结构就必须写,如果这里写了,那么后面实现该trait的结构就可以不写fn test(){println!("不写self参数,则只能通过 :: 的方式进行调用");}
}struct triangle{ //为了简单,假设其是直角三角形,存放两个直角边a: f32,b: f32,
}impl Shape for triangle {fn area(&self) -> f32 {return (self.a*self.b)/2.0;}
}struct square{a: f32
}impl Shape for square {fn area(&self) -> f32 {return self.a*self.a;}
}

通过 trait Shape,使得 triangle、square 都具有了 area 方法。

调用方式:

fn main() {let t=triangle{a: 1.0, b: 2.0};let s=square{a:4.0};//调用带有self参数的函数t.area();s.area();//调用没有self参数的函数triangle::test();square::test();
}

其中area函数的参数带有self,也就是要与具体的结构体对应,调用的时候要用.的方式。

另一种调用方式:

fn main() {let t=triangle{a: 1.0, b: 2.0};let s=square{a:4.0};//调用带有self参数的函数test_area(&t);test_area(&s);
}fn test_area(shape: &impl Shape){shape.area();
}

用 test_area 函数俩输出,这个函数的参数为 &impl Shape,意思是:接受实现了这个 Shape trait 的结构体的引用。

这是不是就和println!宏非常像了!现在只要你的任意形状结构体实现了这个Shape的trait,那么我就能用一个统一的方法(test_area)来输出你的内容!

所以:

只要你自定义了一个结构体,你想要让他可以被println!打印出来,你就得为其实现这个trait

如果要拷贝,那就请你实现Clone这个trait,并且显式的调用clone这个函数,让你自己清楚的认识到此刻你是在完成一个拷贝数据的工作

#[derive(Clone)]
struct Stu{name: String,age:u32
}fn main() {let s1=Stu{name:String::from("yushi-"),age:100};let s2=s1.clone(); //让你能清醒的认识到自己在完成一个拷贝的工作println!("{}:{}", s1.name,s1.age); //可用,因为是将内容拷贝给了s2一份println!("{}:{}", s2.name,s2.age);
}

最常用的trait,除了CopyClone,还有三个:DebugDefaultPartialEq

其中,Debug是方便我们调试用的:

#[derive(Debug)]
struct Stu{name: String,age:u32
}
fn main() {let s1=Stu{/*省略代码*/};println!("{:?}", s1);
}

只要你用了Debug这个trait,那么你就无需实现Display这个trait,也可以方便的打印出相关信息

唯一需要注意的点就是,打印Debug信息,你需要在{}中添加:?

如果你还想要打印格式化后的格式信息,让结构更好看,还可以这样写:

println!("{:#?}", s1);
2、Vec

Vec 是一种动态数组,它可以在运行时自动调整大小。

Vec是Rust标准库的一部分,提供了一种高效、安全的方式来处理大量数据。

基于堆内存申请的连续动态数据类型,其索引、压入(push)、弹出(pop) 操作的时间复杂度为 O(1) 。

Vec 是 vector 的缩写。

Vec的底层实现是基于数组的,因此它的性能非常高。Vec可以存储任何类型的数据,包括整数、浮点数、字符串等。

Vec其实是一个智能指针,用于在堆上分配内存的动态数组。它提供了一些方法来操作数组,如添加、删除和访问元素。与C或Python中的数组不同,Vec会自动处理内存分配和释放,从而避免了常见的内存泄漏和悬挂指针错误。

Vec的本质就是一个三元组,指针、长度、容量,在rust标准库中的定义如下:

pub struct Vec<T, A: Allocator = Global> {buf: RawVec<T, A>,len: usize,
}
impl<T> Vec<T> {#[inline]pub const fn new() -> Self {Vec { buf: RawVec::NEW, len: 0 }}
//...略...
}

Vec的核心功能之一是动态增长和收缩。当向Vec中添加元素时,如果堆上的内存不足,Vec会自动分配更多的内存来容纳元素。这个过程称为“扩容”。同样,当从Vec中删除元素时,如果堆上的内存过多,Vec会自动收缩以释放内存。这个过程称为“缩容”。这种自动内存管理机制使得使用Vec变得非常方便,同时也避免了手动管理内存的错误。

除了基本的添加、删除和访问元素操作之外,Vec还提供了许多其他功能。例如,它们可以按索引访问元素,可以使用迭代器遍历元素,并且支持多种方法(如push()、pop()、insert()和remove())来修改Vec的内容。Vec还提供了一些有用的静态方法(如capacity()、len()和is_empty()),可以用来获取Vec的属性。

虽然Vec是一个非常强大的数据结构,但它们也有一些限制。例如,Vec在堆上分配内存,这意味着访问元素的速度可能会比在栈上分配内存的数组慢。此外,由于Vec是智能指针,因此它们的大小不是固定的,这可能会导致一些编程错误。例如,如果尝试将Vec赋值给一个固定大小的数组或另一个Vec,则会发生编译时错误。

在这里插入图片描述

Vec::new()方法

只创建一个空列表时,必须注明类型(否则通不过编译)。

fn main() {let vec: Vec<i32> = Vec::new();println!("{:?}", vec);
}
Vec::from()方法
 let vec = Vec::from([1,2,3]);
vec! 宏

用于判断是否相等

fn main() {let vec1 = Vec::from([1,2,3]);println!("{:?}", vec1);let vec2 = vec![1,2,3];println!("{:?}", vec2);assert_eq!(vec1, vec2);assert_eq!(vec1, [1,2,3]);assert_eq!(vec2, [1,2,3]);println!("{}", vec1 == vec2); // 输出 true
}

创建相同元素 n 的 vec

fn main() {let vec = vec![0; 5];assert_eq!(vec, [0, 0, 0, 0, 0]);println!("{:?}", vec);let vec = vec![1; 3];assert_eq!(vec, [1, 1, 1]);println!("{:?}", vec);let vec = vec![1; 0];
}

因为是数组,所以还有 pop、splice、sort 等等数组具有的方法。

3、impl

**impl是一个关键字,用于在类型上实现方法。它是将函数与特定类型(结构体或枚举)关联起来的一种方式。impl**主要有两种用途:

1、实现方法:你可以为特定类型定义方法。然后可以在该类型的实例上调用这些方法。

struct Rectangle {width: u32,height: u32,
}impl Rectangle {fn area(&self) -> u32 {self.width * self.height}
}

在这个示例中,为**Rectangle结构体实现了一个名为area**的方法,用于计算矩形的面积。

2、实现特质(Traits):Rust中的特质(Trait)类似于其他语言中的接口。它们定义了类型必须提供的功能。使用**impl**,你可以为特定类型实现一个特质,提供特质中定义的必要方法。

trait Describable {fn describe(&self) -> String;
}impl Describable for Rectangle {fn describe(&self) -> String {format!("Rectangle of width {} and height {}", self.width, self.height)}
}

在这里,为**Rectangle实现了Describable**特质,提供了描述矩形的具体方式。

impl 块中定义的函数可以是独立的,这意味着将其称为 Foo::bar()。 如果函数以 self&self&mut self 作为它的第一个参数,那么也可以使用方法调用语法调用它,这是任何面向对象的程序员都熟悉的特性,比如 foo.bar ()

4、Self

通常在 Rust 的 trait 和 associated function 中使用 Self 来指代实现该 trait 或调用该 associated function 的类型。

struct Point {x: f32,y: f32,
}impl Point {//关联函数fn origin() -> Self {Point { x: 0.0, y: 0.0 }}
}fn main() {let p = Point::origin();
}
5、self

self 是一个代表**类型实例(或者是类型的引用或者是值)**的关键字,在 Rust 的方法中使用 self 可以引用当前类型的实例或者类型本身。

具体来说,当我们定义一个方法时,使用 self 关键字作为方法的第一个参数可以让我们在调用该方法时直接访问类型实例本身

struct Point {x: f32,y: f32,
}impl Point {fn distance(&self, other: &Point) -> f32 {let dx = self.x - other.x;let dy = self.y - other.y;(dx * dx + dy * dy).sqrt()}
}
6、 . 和 ::

在Rust中,.::操作符都可以用来调用方法,但它们的用法有所不同。

.操作符用于调用实例方法。实例方法是定义在类型上的方法,它需要一个类型的实例作为第一个参数(通常称为self)。**而实例方法(instance methods)与其他语言中的动态方法(dynamic methods)类似。都需要先声明一个实例后,才可以用的方法。**例如,下面是一个简单的结构体和一个实例方法的示例:

在这里插入图片描述

上面的代码定义了一个名为Point的结构体,它有两个字段xy。然后,我们在impl Point块中定义了一个名为distance_from_origin的实例方法。这个方法接受一个名为self的参数,它表示调用该方法的实例。在这个方法中,我们使用了self.xself.y来访问实例的字段。

main函数中,我们创建了一个名为pPoint实例,并使用.操作符来调用它的实例方法。也就是说,我们使用了语句p.distance_from_origin()来调用该方法。

::操作符则用于调用关联函数。**关联函数也是定义在类型上的函数,但它不需要一个类型的实例作为第一个参数。Rust中的关联函数(associated functions)与其他语言中的静态方法(static methods)类似。**例如,下面是一个简单的结构体和一个关联函数的示例:

在这里插入图片描述

上面的代码定义了一个名为Point的结构体,它有两个字段xy。然后,我们在impl Point块中定义了一个名为new的关联函数。这个函数接受两个参数:x和y,并返回一个新创建的Point实例。

在main函数中,我们使用::操作符来调用Point类型上的关联函数。也就是说,我们使用了语句Point::new(3, 4)来调用该函数。

实例方法通常用于操作类型的实例。例如,您可以定义一个Point结构体,它有两个字段xy,然后定义一个实例方法来计算点到原点的距离。这个方法需要一个Point类型的实例作为第一个参数,然后使用这个实例的字段来进行计算。

关联函数通常用于执行与类型相关但不依赖于类型实例的操作。例如,您可以定义一个关联函数来创建一个新的Point实例。这个函数不需要一个Point类型的实例作为第一个参数,而是接受一些参数来初始化新创建的实例。

在选择使用实例方法还是关联函数时,您应该考虑您要执行的操作是否依赖于类型的实例。如果是,则应该使用实例方法;否则,应该使用关联函数。

7、self 和 &self、mut 和 &mut

&self,表示向函数传递的是一个引用,不会发生对象所有权的转移;

self,表示向函数传递的是一个对象,会发生所有权的转移,对象的所有权会传递到函数中。

let b = a;
含义:a绑定的资源A转移给b,b拥有这个资源A

let b = &a;  
含义:a绑定的资源A借给b使用,b只有资源A的读权限

let b = &mut a;  
含义:a绑定的资源A借给b使用,b有资源A的读写权限

let mut b = &mut a;  
含义:a绑定的资源A借给b使用,b有资源A的读写权限。同时,b可绑定到新的资源上面去(更新绑定的能力)

fn do(c: String) {}  
含义:传参的时候,实参d绑定的资源D的所有权转移给c

fn do(c: &String) {}  
含义:传参的时候,实参d将绑定的资源D借给c使用,c对资源D只读

fn do(c: &mut String) {}  
含义:传参的时候,实参d将绑定的资源D借给c使用,c对资源D可读写

fn do(mut c: &mut String) {}  
含义:传参的时候,实参d将绑定的资源D借给c使用,c对资源D可读写。同时,c可绑定到新的资源上面去(更新绑定的能力)

8、Option<T>

Option<T> 是 Rust 中的类型系统,来传播和处理错误的类型。

pub enum Option<T> {None,Some(T),
}

Option<T>是一个枚举类型,要么是Some<T>,要么是None。这能很好地表达有值和无值两种情况,避免出现Java中的NullPointerException

9、’ 生命周期标记

生命周期用单引号’加字母表示,置于&后,如&'a、&mut 't

10、unwrap

有的时候我们不想处理或者让程序自己处理 Err, 有时候我们只要 OK 的具体值就可以了。

针对这两种处女座诉求, Rust 语言的开发者们在标准库中定义了两个帮助函数 unwrap()expect()

方法原型说明
unwrapunwrap(self):T如果 selfOkSome 则返回包含的值。 否则会调用宏 panic!() 并立即退出程序
expectexpect(self,msg:&str):T如果 selfOkSome 则返回包含的值。 否则调用panic!() 输出自定义的错误并退出

expect() 函数用于简化不希望事情失败的错误情况。而 unwrap() 函数则在返回 OK 成功的情况下,提取返回的实际结果。

unwrap()expect() 不仅能够处理 Result <T,E> 枚举,还可以用于处理 Option <T> 枚举。

fn main(){let result = is_even(10).unwrap();println!("result is {}",result);println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {if no%2==0 {return Ok(true);} else {return Err("NOT_AN_EVEN".to_string());}
}

编译运行以上 Rust 代码,输出结果如下

thread 'main' panicked at 'called `Result::unwrap()` on 
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace
11、'_ 匿名生命周期

Rust 2018 允许你明确标记生命周期被省略的地方,对于此省略可能不清楚的类型。 要做到这一点,你可以使用特殊的生命周期'_,就像你可以用语法 let x:_ = ..;明确标记一个类型一样。

要我们说的话,无论出于什么原因,我们在 &'a str 周围有一个简单的封装:

struct StrWrap<'a>(&'a str);

Rust 版本指南 中文版

3、栈的 Rust 代码实现、运行结果

stack.rs

/** @Description: * @Author: tianyw* @Date: 2023-12-10 17:43:34* @LastEditTime: 2023-12-10 21:28:31* @LastEditors: tianyw*/
#[derive(Debug)] // Debug 是派生宏的名称,此语句为 Stack 结构体实现了 Debug traitpub struct Stack<T> { // pub 表示公开的size: usize, // 栈大小data: Vec<T>, // 栈数据 泛型数组
}impl<T> Stack<T> { // impl 用于定义类型的实现,如实现 new 方法、is_empty 方法等// 初始化空栈pub fn new() -> Self { // 指代 Stack 类型Self {size: 0,data: Vec::new() // 初始化空数组}}pub fn is_empty(&self) -> bool {0 == self.size // 结尾没有分号,表示返回当前值}pub fn len(&self) -> usize { // &self 只可读self.size // 结尾没有分号 表示返回当前值}// 清空栈pub fn clear(&mut self) { // &mut self 可读、可写self.size = 0;self.data.clear();}// 将数据保存在 Vec 的末尾pub fn push(&mut self, val:T) {self.data.push(val);self.size +=1;}// 在将栈顶减1后,弹出数据pub fn pop(&mut self) -> Option<T> {if 0 == self.size { return None; }self.size -= 1;self.data.pop()}// 返回栈顶数据引用和可变引用pub fn peek(&self) -> Option<&T> {if 0 == self.size {return None;}self.data.get(self.size - 1) // 不带分号 获取值并返回}pub fn peek_mut(&mut self) -> Option<&mut T> {if 0 == self.size {return None;}self.data.get_mut(self.size - 1)}// 以下是为栈实现的迭代功能// into_iter:栈改变,成为迭代器// iter: 栈不变,得到不可变迭代器// iter_mut: 栈不变,得到可变迭代器pub fn into_iter(self) -> IntoIter<T> {IntoIter(self)}pub fn iter(&self) -> Iter<T> {let mut iterator = Iter { stack: Vec::new() };for item in self.data.iter() {iterator.stack.push(item);}iterator}pub fn iter_mut(&mut self) -> IterMut<T> {let mut iterator = IterMut { stack: Vec::new() };for item in self.data.iter_mut() {iterator.stack.push(item);}iterator}}// 实现三种迭代功能
pub struct IntoIter<T>(Stack<T>);
impl<T:Clone> Iterator for IntoIter<T> {type Item = T;fn next(&mut self) -> Option<Self::Item> {if !self.0.is_empty() {self.0.size -= 1;self.0.data.pop()} else {None}}
}pub struct Iter<'a,T:'a> { stack: Vec<&'a T>, }
impl<'a,T> Iterator for Iter<'a,T> {type Item = &'a T;fn next(&mut self) -> Option<Self::Item> {self.stack.pop()}
}pub struct IterMut<'a,T:'a> { stack: Vec<&'a mut T> }
impl<'a,T> Iterator for IterMut<'a,T> {type Item = &'a mut T;fn next(&mut self) -> Option<Self::Item> {self.stack.pop()}
}    

main.rs

mod stack;
fn main() {basic();peek();iter();fn basic() {let mut s= stack::Stack::new();s.push(1);s.push(2);s.push(3);println!("size:{},{:?}", s.len(), s);println!("pop {:?},size {}", s.pop().unwrap(), s.len());println!("empty: {}, {:?}", s.is_empty(), s);s.clear();println!("{:?}", s);}fn peek() {let mut s = stack::Stack::new();s.push(1);s.push(2);s.push(3);println!("{:?}", s);let peek_mut = s.peek_mut();if let Some(top) = peek_mut {*top = 4;}println!("top {:?}", s.peek().unwrap());println!("{:?}", s);}fn iter() {let mut s = stack::Stack::new();s.push(1);s.push(2);s.push(3);let sum1 = s.iter().sum::<i32>();let mut addend = 0;for item in s.iter_mut() {*item += 1;addend += 1;}let sum2 = s.iter().sum::<i32>();println!("{sum1} + {addend} = {sum2}");assert_eq!(9, s.into_iter().sum::<i32>());}
}

cargo run 运行结果

在这里插入图片描述

这里使用集合容器Vec作为栈的底层实现,因为Rust中的Vec提供了有序集合机制和一组操作方法,只需要选定Vec的哪一端是栈顶就可以实现其他操作了。以下栈实现假定Vec的尾部保存了栈的顶部元素,随着栈不断增长,新项将被添加到Vec的末尾。因为不知道所插入数据的类型,所以采用泛型数据类型T。此外,为了实现迭代功能,这里添加了IntoIter、Iter、IterMut三个结构体,以分别完成三种迭代功能。

应用:括号匹配、加减乘除优先级匹配

// par_checker3.rsfn par_checker3(par: &str) -> bool {let mut char_list = Vec::new();for c in par.chars() { char_list.push(c); }let mut index = 0;let mut balance = true;let mut stack = Stack::new();while index < char_list.len() && balance {let c = char_list[index];// 将开始符号入栈if '(' == c || '[' == c || '{' == c {stack.push(c);}// 如果是结束符号,则判断是否平衡if ')' == c || ']' == c || '}' == c {if stack.is_empty() {balance = false;} else {let top = stack.pop().unwrap();if !par_match(top, c) { balance = false; }}}// 非括号字符直接跳过index += 1;}balance && stack.is_empty()
}fn main() {let sa = "(2+3){func}[abc]"; let sb = "(2+3)*(3-1";let res1 = par_checker3(sa); let res2 = par_checker3(sb);println!("sa balanced:{res1}, sb balanced:{res2}");// (2+3){func}[abc] balanced:true, (2+3)*(3-1 balanced:false
}

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

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

相关文章

【Hadoop_04】HDFS的API操作与读写流程

1、HDFS的API操作1.1 客户端环境准备1.2 API创建文件夹1.3 API上传1.4 API参数的优先级1.5 API文件夹下载1.6 API文件删除1.7 API文件更名和移动1.8 API文件详情和查看1.9 API文件和文件夹判断 2、HDFS的读写流程&#xff08;面试重点&#xff09;2.1 HDFS写数据流程2.2 网络拓…

https 协议

目录 加密方式 对称加密 非对称加密 非对称加密 非对称加密 非对称加密 对称加密 AC证书 AC证书内容 数据摘要 数据签名 在我们前面学习的http协议里面&#xff0c;我们发送的内容都是明文传输的&#xff0c;所以在安全上并不安全&#xff0c;但是在现在信息发达的时…

Linux之Apache服务器安装及配置

一、Apache服务器简介 Apache HTTP Server&#xff08;简称Apache&#xff09;是Apache软件基金会的一个开放源码的网页服务器&#xff0c;可以在大多数计算机操作系统中运行&#xff0c;由于其多平台和安全性被广泛使用。Apache曾经是世界使用排名第一的Web服务器软件&#xf…

VSCODE连接远程服务器

安装ssh插件 根据你的操作系统选择对应的版本进行下载和安装。 安装完成之后&#xff0c;启动vscode&#xff0c;选择左侧Extensions 选项卡&#xff0c;在输入框搜索 remote &#xff0c;选择安装Remote-SSH插件。 安装完成之后会在左侧新增一个选项卡Remote Explorer&#xf…

肥猫游戏报价器|计价器|王者荣耀代练陪练等游戏报价器软件介绍说明

目录 1. 前言2. 软件著作权3. 软件使用说明3.1 进入软件3.2 用户登录3.3 首页3.4 报价器3.4.1 总体介绍3.4.2 王者报价器3.4.3 LOL手游报价器3.4.4 英雄联盟报价器3.4.5 云顶之弈报价器3.4.7 王者水晶报价器3.4.8 和平精英报价器3.4.9 蛋仔派对报价器3.4.10 穿越火线报价器3.4.…

kafka学习笔记--broker工作流程、重要参数

本文内容来自尚硅谷B站公开教学视频&#xff0c;仅做个人总结、学习、复习使用&#xff0c;任何对此文章的引用&#xff0c;应当说明源出处为尚硅谷&#xff0c;不得用于商业用途。 如有侵权、联系速删 视频教程链接&#xff1a;【尚硅谷】Kafka3.x教程&#xff08;从入门到调优…

TypeScript 常用高级类型

目录 前言&#xff1a; TypeScript 常用高级类型 基本概念 高级类型 1. 交叉类型&#xff08;Intersection Types&#xff09; 2. 联合类型&#xff08;Union Types&#xff09; 3. 映射类型&#xff08;Mapped Types&#xff09; 4. 条件类型&#xff08;Conditional…

Pytorch-Transformer轴承故障一维信号分类(三)

目录 前言 1 数据集制作与加载 1.1 导入数据 第一步&#xff0c;导入十分类数据 第二步&#xff0c;读取MAT文件驱动端数据 第三步&#xff0c;制作数据集 第四步&#xff0c;制作训练集和标签 1.2 数据加载&#xff0c;训练数据、测试数据分组&#xff0c;数据分batch…

据房间Id是否存在,判断当前房间是否到期且实时更改颜色

重点代码展示&#xff1a; <template><el-col style"width: 100%;height: 100%;"><el-col :span"20"><el-card class"room_info"><avue-data-icons :option"option"></avue-data-icons></el-…

RT-DETR算法优化改进:轻量化自研设计双卷积重新设计backbone和neck,完成涨点且计算量和参数量显著下降

💡💡💡本文自研创新改进:双卷积由组卷积和异构卷积组成,执行 33 和 11 卷积运算代替其他卷积核仅执行 11 卷积,YOLOv8 Conv,从而轻量化RT-DETR,性能如下表,GFLOPs 8.1降低至7.6,参数量6.3MB降低至5.8MB RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_637742…

ubuntu-c++-可执行模块-动态链接库-链接库搜索-基础知识

文章目录 1.动态链接库简介2.动态库搜索路径3.运行时链接及搜索顺序4.查看可运行模块的链接库5.总结 1.动态链接库简介 动态库又叫动态链接库&#xff0c;是程序运行的时候加载的库&#xff0c;当动态链接库正确安装后&#xff0c;所有的程序都可以使用动态库来运行程序。动态…

Android帝国之日志系统--logd、logcat

本文概要 这是Android系统进程系列的第四篇文章&#xff0c;本文以自述的方式来介绍logd进程&#xff0c;通过本文您将了解到logd进程存在的意义&#xff0c;以及日志系统的实现原理。&#xff08;文中的代码是基于android13&#xff09; Android系统进程系列的前三篇文章如下…

如何在OpenWRT软路由系统部署uhttpd搭建web服务器实现远程访问——“cpolar内网穿透”

文章目录 前言1. 检查uhttpd安装2. 部署web站点3. 安装cpolar内网穿透4. 配置远程访问地址5. 配置固定远程地址 前言 uhttpd 是 OpenWrt/LuCI 开发者从零开始编写的 Web 服务器&#xff0c;目的是成为优秀稳定的、适合嵌入式设备的轻量级任务的 HTTP 服务器&#xff0c;并且和…

docker-compose的介绍与使用

一、docker-compose 常用命令和指令 1. 概要 默认的模板文件是 docker-compose.yml&#xff0c;其中定义的每个服务可以通过 image 指令指定镜像或 build 指令&#xff08;需要 Dockerfile&#xff09;来自动构建。 注意如果使用 build 指令&#xff0c;在 Dockerfile 中设置…

RHEL网络服务器

目录 1.时间同步的重要性 2.配置时间服务器 &#xff08;1&#xff09;指定所使用的上层时间服务器。 (2&#xff09;指定允许访问的客户端 (3&#xff09;把local stratum 前的注释符#去掉。 3.配置chrony客户端 &#xff08;1&#xff09;修改pool那行,指定要从哪台时间…

Python常见面试知识总结(一):迭代器、拷贝、线程及底层结构

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来总结一下Python和C语言中常见的面试知识&#xff0c;欢迎大家一起前来探讨学习~ 【一】Python中迭代器的概念&#xff1f; 可迭代对象是迭代器、生成器和装饰器的基础。简单来说&#xff0c;可以使用for来循环遍历…

[古剑山2023] pwn

最近这个打stdout的题真多。这个比赛没打。拿到附件作了一天。 choice 32位&#xff0c;libc-2.23-i386&#xff0c;nbytes初始值为0x14,读入0x804A04C 0x14字节后会覆盖到nbytes 1个字节。当再次向v1读入nbytes字节时会造成溢出。 先写0x14p8(0xff)覆盖到nbytes然后溢出写传…

记录一次postgresql临时表丢失问题

项目相关技术栈 springboot hikari连接池pgbouncerpostgresql数据库 背景 为了优化一个任务执行的速度&#xff0c;我将任务的sql中部分语句抽出生成临时表&#xff08;create temp table tempqw as xxxxxxxxx&#xff09;&#xff0c;再和其他表关联&#xff0c;提高查询速…

三翼鸟2023辉煌收官, 定盘2024高质量棋局

最近在不同平台上接连看到这样的热搜话题&#xff1a;用时间胶囊记录2023的自己、2023年度问答、2023十大网络流行语公布… 显然&#xff0c; 2023年进入最后一个月&#xff0c;时间匆匆&#xff0c;这也意味着又到了总结过去和规划未来的时候。拿到结果、取得成绩当然是对202…

短视频引流获客系统:引领未来营销的新潮流

在这个信息爆炸的时代&#xff0c;短视频已经成为了人们获取信息的主要渠道之一。而随着短视频的火爆&#xff0c;引流获客系统也逐渐成为了营销领域的新宠。本文将详细介绍短视频引流获客系统的开发流程以及涉及到的技术&#xff0c;让我们一起来看看这个引领未来营销的新潮流…