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

news2024/11/17 13:47:38

数据结构与算法-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 trait

pub 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.rs
 
fn 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.coloradmin.cn/o/1303267.html

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

相关文章

目标检测——R-FCN算法解读

论文&#xff1a;R-FCN: Object Detection via Region-based Fully Convolutional Networks 作者&#xff1a;Jifeng Dai, Yi Li, Kaiming He and Jian Sun 链接&#xff1a;https://arxiv.org/pdf/1605.06409v2.pdf 代码&#xff1a;https://github.com/daijifeng001/r-fcn 文…

【开源】基于Vue和SpringBoot的森林火灾预警系统

项目编号&#xff1a; S 019 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S019&#xff0c;文末获取源码。} 项目编号&#xff1a;S019&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 系统基础模块2.3 烟…

CodeGeeX发布HBuilderX插件,助力VUE开发效率提升

北京时间2023年12月8日&#xff0c;CodeGeeX正式发布了适配国产IDE平台HBuilderX的插件。这款插件的推出&#xff0c;使得使用HBuilderX作为开发环境的程序员可以在IDE和AI辅助编程工具之间做出选择。 CodeGeeX&#xff1a;基于大模型的AI智能编程助理 CodeGeeX是一款基于大模…

Hbase2.5.5分布式部署安装记录

文章目录 1 环境准备1.1 节点部署情况1.2 安装说明 2 Hbase安装过程Step1&#xff1a;Step2:Step3:Step4&#xff1a; 3 Web UI检查状态并测试3.1 Web UI3.2 创建测试命名空间 1 环境准备 1.1 节点部署情况 Hadoop11&#xff1a;Hadoop3.1.4 、 zookeeper3.4.6、jdk8 Hadoop1…

【JavaWeb学习专栏 | CSS篇】css简单介绍 css常用选择器集锦

个人主页&#xff1a;[兜里有颗棉花糖(https://xiaofeizhu.blog.csdn.net/) 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaWeb学习专栏】【Java系列】 希望本文内容可以帮助到大家&#xff0c;一起加油吧&#xff01;…

IP与以太网的转发操作

TCP模块在执行连接、收发、断开等各阶段操作时&#xff0c;都需要委托IP模块将数据封装成包发送给通信对象。 网络中有路由器和集线器两种不同的转发设备&#xff0c;它们在传输网络包时有着各自的分工。 (1)路由器根据目标地址判断下一个路由器的位置 (2)集线器在子网中将网…

Redis为什么是单线程的?

Redis为什么是单线程的&#xff1f; 1.代码更清晰&#xff0c;处理逻辑更简单&#xff1b; 不2.用考虑各种锁的问题&#xff0c;不存在加锁和释放锁的操作&#xff0c;没有因为可能出现死锁而导致的性能问题&#xff1b; 3.不存在多线程切换而消耗CPU&#xff1b; 4.无法发挥多…

2023年医疗器械行业分析(京东医疗器械运营数据分析):10月销额增长53%

随着我国整体实力的增强、国民生活水平的提高、人口老龄化、医疗保障体系不断完善等因素的驱动&#xff0c;我国的医疗器械市场增长迅速。 根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年10月份&#xff0c;京东平台上医疗器械市场的销量将近1200万&#xff0c;环比…

IDE代码编辑器:CLion 2023.3(WinMac)中文激活版

CLion 2023是一款由JetBrains公司为C/C编程设计的跨平台集成开发环境&#xff08;IDE&#xff09;。它集成了丰富的功能和工具&#xff0c;旨在帮助开发人员更高效地进行C/C编程和调试。 以下是这款软件的一些主要特点和功能&#xff1a; 高效的编程体验&#xff1a;为Mac用户提…

HarmonyOS编译开源native库(OpenSSL实例)

前言 近期项目要开始做鸿蒙版本&#xff0c;有一部分依赖native的代码也需要迁移&#xff0c;某个native模块依赖openssl&#xff0c;需要在鸿蒙下重新编译openssl才行。一开始找了很多相关文档都没有得到方法&#xff0c;无奈只能自己凭经验慢慢试&#xff0c;最后还是成功了…

javascript和HTML手机端实现多条件筛选的实战记录(筛选层的展示与隐藏、AJAX传输数组)

实现多条件筛选功能在JavaScript和HTML中可以分为以下几个步骤&#xff1a; HTML页面布局: 设计你的页面布局&#xff0c;包括筛选条件的选择器和结果展示区域。‘’ JavaScript逻辑:通过JavaScript监听筛选条件的变化&#xff0c;并根据选择的值对结果进行筛选。动态展示: 实…

编辑器Sublime text 常用快捷命令 列模式 替换空行

平替notepad 下载可取官网 www.sublimetext.com 据说可以无限试用&#xff0c;没有功能限制 1、快速删除空行 ctrl h选择正则表达式 .*Find输入&#xff1a; ^(\t)*$\nReplace输入&#xff1a;点击Replace All 2、快速选择指定字符 用鼠标选中alt f3修改 3、列编辑模式 ct…

亿道三防平板/手持终端/工业笔记本/车载电脑配件指南,使用高效加倍!

以前我们在选购合适的三防加固计算机时&#xff0c;总是在强调项目的规格参数需求&#xff0c;强调三防平板/手持终端/工业笔记本/车载电脑等终端设备的性能和功能。然而&#xff0c;我们常常忽略了一个重要的维度&#xff1a;用户体验。三防加固计算机作为数字化基础设施和生产…

Spark与PySpark(1.概述、框架、模块)

目录 1.Spark 概念 2. Hadoop和Spark的对比 3. Spark特点 3.1 运行速度快 3.2 简单易用 3.3 通用性强 3.4 可以允许运行在很多地方 4. Spark框架模块 4.1 Spark Core 4.2 SparkSQL 4.3 SparkStreaming 4.4 MLlib 4.5 GraphX 5. Spark的运行模式 5.1 本地模式(单机) Local运行模…

ubuntu18.04配置cuda+cudnn+tensorrt+anconda+pytorch-gpu+pycharm

一、显卡驱动安装 执行nvidia-smi查看安装情况 二、cuda安装 cuda官网下载cuda_11.6.2_510.47.03_linux.run&#xff0c;安装执行 sudo sh cuda_11.6.2_510.47.03_linux.run提升安装项&#xff0c;驱动不用安装&#xff0c;即第一项&#xff08;Driver&#xff09;&#xff…

SpringBoot集成swagger2配置权限认证参数

作者简介&#xff1a;大家好&#xff0c;我是撸代码的羊驼&#xff0c;前阿里巴巴架构师&#xff0c;现某互联网公司CTO 联系v&#xff1a;sulny_ann&#xff08;17362204968&#xff09;&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗…

【JavaWeb学习笔记】8 - HTTP

一、常用文档 请求头 响应头 中间件获取的网页协议和返回的内容 这些称为HTTP协议 请求和响应 常见的请求头 响应头 状态码 HTTP状态码 当浏览者访问一个网页时&#xff0c;浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前&#xff0c;此网页所在的服务…

【二者区别】cuda和cudatoolkit

Pytorch 使用不同版本的 cuda 由于课题的原因&#xff0c;笔者主要通过 Pytorch 框架进行深度学习相关的学习和实验。在运行和学习网络上的 Pytorch 应用代码的过程中&#xff0c;不少项目会标注作者在运行和实验时所使用的 Pytorch 和 cuda 版本信息。由于 Pytorch 和 cuda 版…

联邦蒸馏中的分布式知识一致性 | TIST 2024

联邦蒸馏中的分布式知识一致性 | TIST 2024 联邦学习是一种隐私保护的分布式机器学习范式&#xff0c;服务器可以在不汇集客户端私有数据的前提下联合训练机器学习模型。通信约束和系统异构是联邦学习面临的两大严峻挑战。为同时解决上述两个问题&#xff0c;联邦蒸馏技术被提…

vue 中国省市区级联数据 三级联动

vue 中国省市区级联数据 三级联动 安装插件 npm install element-china-area-data5.0.2 -S 当前版本以测试&#xff0c;可用。组件中使用了 element-ui, https://element.eleme.cn/#/zh-CN/component/installation 库 请注意安装。插件文档 https://www.npmjs.com/package/ele…