데이터 구조 및 알고리즘-Rust Edition 읽기 노트-2 선형 데이터 구조-스택

데이터 구조 및 알고리즘-Rust Edition 읽기 노트-2 선형 데이터 구조-스택

1. 선형 데이터 구조의 개념

배열, 스택, 큐, 이중 큐, 연결 목록 등의 데이터 구조는 모두 데이터를 저장하는 컨테이너이며, 데이터 항목 간의 순서는 추가 또는 삭제되는 순서에 따라 결정됩니다. 앞과 뒤의 요소에 따라 변경됩니다. 위치를 일정하게 유지하는 데이터 구조를 선형 데이터 구조라고 합니다.

선형 자료 구조는 "left"와 "right"라는 두 개의 끝을 가지고 있습니다. 어떤 경우에는 "front"와 "back"이라고도 합니다. 물론 top과 Bottom으로 불릴 수도 있습니다. 이름은 중요하지 않습니다. , 중요한 것은 이 명명으로 표시되는 위치 관계는 데이터가 선형 방식으로 구성되어 있음을 나타냅니다. 이러한 선형적 특성은 메모리와 밀접한 관련이 있습니다. 메모리는 일종의 선형 하드웨어이기 때문이며, 소프트웨어와 하드웨어가 어떻게 연관되어 있는지도 알 수 있습니다.

선형 데이터 구조는 데이터가 저장되는 방식이 아니라 데이터에 액세스하는 방식에 관한 것입니다.

선형 데이터 구조가 반드시 데이터 항목이 메모리에서 인접해 있다는 것을 의미하지는 않습니다. 연결된 목록을 예로 들면, 데이터 항목이 메모리의 다양한 위치에 있을 수 있지만 액세스는 선형입니다.

다양한 선형 데이터 구조를 구별하는 한 가지 방법은 데이터 항목을 추가하고 제거하는 방법, 특히 데이터 항목이 추가되고 제거되는 위치를 살펴보는 것입니다. 예를 들어, 일부 데이터 구조에서는 한쪽 끝에서만 항목을 추가할 수 있고, 다른 데이터 구조에서는 반대쪽 끝에서 항목을 제거할 수 있으며, 또 다른 데이터 구조에서는 양쪽 끝에서 항목을 조작할 수 있습니다. 이러한 변형과 ​​이들의 결합된 형태는 컴퓨터 과학 분야에서 매우 유용한 많은 데이터 구조를 생성했으며, 다양한 알고리즘에 나타나 다양한 실용적이고 중요한 작업을 수행합니다.

1. 스택: 마지막에 들어간 것, 먼저 나온 것

스택은 새로운 항목의 추가 및 제거가 항상 동일한 끝(상단)에서 발생하고 반대쪽 끝을 하단(하단)이라고 하는 정렬된 데이터 항목 모음입니다. 스택의 맨 아래에 있는 항목이 가장 오래 저장된 항목이고 가장 최근에 추가된 항목이 가장 먼저 제거되기 때문에 스택의 맨 아래가 중요합니다. 이 정렬 원리를 LIFO(후입선출) 또는 FILO(선입선출)라고도 하며, 새로운 항목이 위쪽에 더 가깝고 오래된 항목이 아래쪽에 더 가깝습니다.

2. Rust에 대한 사전 지식

1、특성

traitJava의 인터페이스, TS의 인터페이스, 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;
    }
}

Shape 특성을 통해 삼각형과 사각형 모두 면적 방법을 갖습니다.

호출 방법:

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();
}

영역 함수의 매개변수에는 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 특성을 구현하는 구조에 대한 참조를 승인한다는 의미입니다.

이것은 매크로와println! 매우 유사하지 않나요? 이제 임의의 모양 구조가 Shape의 특성을 구현하는 한 통합 메소드(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, 还有三个:Debug,Default,PartialEq

그 중Debug 디버깅하기에 편리한 항목은 다음과 같습니다.

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

이것을 사용하는 한 Debugtrait, 그러면 Display 특성을 구현할 필요가 없습니다. 편리합니다. 관련 정보를 인쇄하세요

주의해야 할 유일한 점은 정보를 인쇄하려면Debug 정보를 추가해야 한다는 것입니다{}:?

구조를 더 보기 좋게 만들기 위해 형식화된 형식 정보를 인쇄하려면 다음과 같이 작성할 수도 있습니다.

println!("{:#?}", s1);
2、아이템

Vec은 런타임에 자동으로 크기가 조정되는 동적 배열입니다.

Vec은 Rust 표준 라이브러리의 일부이며 대량의 데이터를 처리하는 효율적이고 안전한 방법을 제공합니다.

힙 메모리 애플리케이션을 기반으로 하는 연속 동적 데이터 유형의 경우 인덱싱, 푸시 및 팝 작업의 시간 복잡도는 O(1)입니다.

Vec은 벡터의 약자입니다.

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은 다른 많은 기능을 제공합니다. 예를 들어, 인덱스로 요소에 액세스할 수 있고, 반복자를 사용하여 요소를 탐색할 수 있으며, Vec의 내용을 수정하기 위한 여러 메서드(예: push(), pop(), insert() 및 Remove())를 지원할 수 있습니다. Vec은 또한 Vec의 속성을 얻는 데 사용할 수 있는 몇 가지 유용한 정적 메서드(예:capacity(), len() 및 is_empty())를 제공합니다.

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**두 가지 주요 용도는 다음과 같습니다.

1. 구현 방법: 특정 유형에 대한 방법을 정의할 수 있습니다. 그런 다음 해당 유형의 인스턴스에서 이러한 메서드를 호출할 수 있습니다.

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

이 예에서는 직사각형의 면적을 계산하기 위해 **Rectangle 구조에 대해area**라는 메서드가 구현되었습니다.

2. 특성 구현: Rust의 특성은 다른 언어의 인터페이스와 유사합니다. 이는 유형이 제공해야 하는 기능을 정의합니다. **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)
    }
}

여기서 **RectangleDescribable** 특성을 구현하여 직사각형을 설명하는 구체적인 방법을 제공합니다.

impl 블록에 정의된 함수는 독립적일 수 있습니다. 즉, Foo::bar() 호출을 의미합니다. 함수가 self, &self 또는 &mut self를 첫 번째 인수로 사용하는 경우 메서드 호출 구문을 사용하여 호출할 수도 있습니다. foo.bar ()와 같은 객체 지향 프로그래머에게 친숙한 기능입니다.

4、자신

Self는 일반적으로 Rust 특성 및 관련 함수에서 특성을 구현하거나 관련 함수를 호출하는 유형을 참조하는 데 사용됩니다.

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는 유형 인스턴스(또는 유형의 참조 또는 값)를 나타내는 키워드입니다. 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이라고 함). **인스턴스 메서드는 다른 언어의 동적 메서드와 유사합니다. 모든 메소드를 사용하려면 먼저 선언해야 합니다. **예를 들어, 다음은 간단한 구조체와 인스턴스 메소드의 예입니다.

여기에 이미지 설명을 삽입하세요.

위 코드는 두 개의 필드가 있는 Point이라는 구조를 정의합니다. 그런 다음 블록에 라는 인스턴스 메서드를 정의합니다. 이 메소드는 메소드가 호출되는 인스턴스를 나타내는 라는 매개변수를 허용합니다. 이 방법에서는 을 사용하여 인스턴스의 필드에 액세스합니다. xyimpl Pointdistance_from_originselfself.xself.y

main 함수에서 p라는 이름의 Point 인스턴스를 만들고 문을 사용하여 이 메소드를 호출합니다. .p.distance_from_origin()

:: 연산자는 관련 함수를 호출하는 데 사용됩니다. **연관 함수는 유형에 정의된 함수이기도 하지만 해당 유형의 인스턴스가 첫 번째 매개변수로 필요하지 않습니다. Rust의 관련 함수는 다른 언어의 정적 메서드와 유사합니다. **예를 들어, 다음은 간단한 구조와 관련 기능의 예입니다.

여기에 이미지 설명을 삽입하세요.

위 코드는 두 개의 필드가 있는 Point이라는 구조를 정의합니다. 그런 다음 블록에 라는 상관 함수를 정의합니다. 이 함수는 x와 y라는 두 개의 매개변수를 받아들이고 새로 생성된 Point 인스턴스를 반환합니다. xyimpl Pointnew

기본 함수에서는 :: 연산자를 사용하여 Point 유형에 연결된 함수를 호출합니다. 즉, 함수를 호출하기 위해 Point::new(3, 4) 문을 사용합니다.

인스턴스 메소드는 일반적으로 유형의 인스턴스에서 작동하는 데 사용됩니다. 예를 들어, 두 개의 필드 가 있는 Point 구조를 정의한 다음 인스턴스 메소드를 정의하여 거리를 계산할 수 있습니다. 원산지에 대한 점. 이 방법에서는 첫 번째 매개변수로 유형의 인스턴스가 필요하며 이 인스턴스의 필드를 사용하여 계산을 수행합니다. xyPoint

연관 함수는 일반적으로 유형과 관련되어 있지만 유형의 인스턴스에 의존하지 않는 작업을 수행하는 데 사용됩니다. 예를 들어, 연관된 함수를 정의하여 새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는 리소스 읽기 및 쓰기 가능

fn do(mut c: &mut String) {}  
의미: 매개변수를 전달할 때 실제 매개변수 d는 바인딩된 리소스 D를 c에 빌려주고, c 리소스 D는 읽기 및 쓰기 가능. 동시에 c는 새로운 리소스에 바인딩될 수 있습니다(바인딩 업데이트 기능)

8、옵션<T>

Option<T>는 잘못된 유형을 전파하고 처리하는 Rust의 유형 시스템입니다.

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

Option<T>은 열거형 유형으로 Some<T> 또는 None입니다. 이는 Java에서 NullPointerException을 피하여 가치 있는 상황과 가치 없는 상황을 모두 잘 표현할 수 있습니다.

9.' 수명주기 태그

라이프 사이클은 &a, &mut 't와 같이 & 뒤에 문자가 오는 작은따옴표 '로 표시됩니다.

10、풀기

가끔 Err 자체적으로 처리하거나 프로그램이 처리하도록 하고 싶지 않은 경우도 있습니다. 때로는 OK의 특정 값만 필요한 경우도 있습니다.

이러한 두 가지 Virgo 요구에 대응하여 Rust 언어 개발자는 표준 라이브러리에 두 가지 도우미 함수 unwrap()expect()를 정의했습니다.

방법 원기 설명하다
풀다 unwrap(self):T selfOk 또는 Some 인 경우 포함된 값을 반환합니다. 그렇지 않으면 panic!() 매크로가 호출되고 프로그램이 즉시 종료됩니다
예상하다 expect(self,msg:&str):T selfOk 또는 Some 인 경우 포함된 값을 반환합니다. 그렇지 않은 경우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>());
    }
}

카고 런 실행 결과

여기에 이미지 설명을 삽입하세요.

여기에서는 컬렉션 컨테이너 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
}

추천

출처blog.csdn.net/yinweimumu/article/details/134936887