2024王道数据结构考研丨第三章:栈和队列

news2024/11/19 7:46:48

2024王道数据结构考研笔记专栏将持续更新,欢迎 点此 收藏,共同交流学习…

文章目录

    • 第三章:栈和队列
      • 3.1栈(stack)
        • 3.1.1栈的基本概念
        • 3.1.2 栈的顺序存储
        • 3.1.3栈的链式存储
      • 3.2队列(Queue)
        • 3.2.1队列的基本概念
        • 3.2.2队列的顺序存储结构
        • 3.2.3队列的链式存储结构
        • 3.2.4双端队列
        • 3.2.5循环队列
      • 3.3栈的应用
        • 3.3.1栈在括号匹配中的应用
        • 3.3.2栈在表达式求值中的应用
        • 3.3.3栈在递归中的应用
      • 3.4特殊矩阵的压缩存储
        • 3.4.1数组的存储结构
        • 3.4.2普通矩阵的存储
        • 3.4.3特殊矩阵的存储


第三章:栈和队列

3.1栈(stack)

3.1.1栈的基本概念

1.栈的定义

  • 栈是特殊的线性表:只允许在一端进行插入或删- 除操作, 其逻辑结构与普通线性表相同;
  • 栈顶(Top):允许进行插入和删除的一端 (最上面的为栈顶元素);
  • 栈底(Bottom):固定的,不允许进行插入和删除的一端 (最下面的为栈底元素);
  • 空栈:不含任何元素的空表;
  • 特点:后进先出(后进栈的元素先出栈);
  • 缺点:栈的大小不可变,解决方法——共享栈;

2.栈的基本操作

“创建&销毁”

  • InitStack(&S) 初始化栈:构造一个空栈S,分配内存空间;
  • DestroyStack(&S) 销毁栈:销毁并释放栈S所占用的内存空间;

“增&删”

  • Push(&S, x) 进栈:若栈S未满,则将x加入使其成为新栈顶;
  • Pop(&S, &x) 出栈:若栈S非空,则弹出(删除)栈顶元素,并用x返回;
    “查&其他”
  • GetTop(S, &x) 读取栈顶元素:若栈S非空,则用x-返回栈顶元素;(栈的使用场景大多只访问栈顶元素);
  • StackEmpty(S) 判空: 断一个栈S是否为空,若S为空,则返回true,否则返回false;

3.1.2 栈的顺序存储

1.顺序栈的定义

#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top;                      //栈顶元素
}SqStack;

void testStack(){
    SqStack S;       //声明一个顺序栈(分配空间)
                     //连续的存储空间大小为 MaxSize*sizeof(ElemType)
}

2.顺序栈的基本操作

#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top;                      //栈顶元素
}SqStack;

//初始化栈
void InitStack(SqStack &S){
    S.top = -1;                   //初始化栈顶指针
}

//判栈空
bool StackEmpty(SqStack S){
    if(S.top == -1)      //栈空
        return true;
    else                 //栈不空
        return false;
}

//新元素进栈
bool Push(SqStack &S, ElemType x){
    if(S.top == MaxSize - 1)        //栈满
        return false;
    
    S.top = S.top + 1;    //指针先加1
    S.data[S.top] = x;    //新元素入栈

    /*
    S.data[++S.top] = x;
    */
    return true;
}

//出栈
bool Pop(SqStack &x, ElemType &x){
    if(S.top == -1)          //栈空
        return false;
    
    x = S.data[S.top];       //先出栈
    S.top = S.top - 1;       //栈顶指针减1
    return true;

    /*
    x = S.data[S.top--];
    */

    //只是逻辑上的删除,数据依然残留在内存里

}

//读栈顶元素
bool GetTop(SqStack S, ElemType &x){
    if(S.top == -1)
        return false;
    
    x = S.data[S.top];      //x记录栈顶元素
    return true; 
}


void testStack(){
    SqStack S;       //声明一个顺序栈(分配空间)
    InitStack(S);
    //...
}

注意: 也可以初始化时定义 S.top = 0 :top指针指向下一个可以插入元素的位置(栈顶元素的后一个位置);

  • 进栈操作 :栈不满时,栈顶指针先加1,再送值到栈顶元素。S.data[S.top++] = x;
  • 出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。`x = S.data[–S.top];
  • 栈空条件:S.top==-1
  • 栈满条件:S.top==MaxSize-1
  • 栈长:S.top+1

3.共享栈
定义: 利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数组空间,将两个栈的栈底 分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。

  • 存取数据的时间复杂度均为O(1)
#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top0;                     //0号栈栈顶指针
    int top1;                     //1号栈栈顶指针
}ShStack;

//初始化栈
void InitSqStack(ShStack &S){
    S.top0 = -1;        //初始化栈顶指针
    S.top1 = MaxSize;   
}


栈满条件:top1-top0=1

3.1.3栈的链式存储

1.定义:采用链式存储的栈称为链栈。
2.优点:链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。
3.特点:

  • 进栈和出栈都只能在栈顶一端进行(链头作为栈顶)
  • 链表的头部作为栈顶,意味着:
    1. 在实现数据"入栈"操作时,需要将数据从链表的头部插入;
    2. 在实现数据"出栈"操作时,需要删除链表头部的首元节点;
    因此,链栈实际上就是一个只能采用头插法插入或删除数据的链表;
    栈的链式存储结构可描述为:
typedef struct Linknode{
    ElemType data;              //数据域
    struct Linknode *next;      //指针域
}*LiStack;                      //栈类型的定义

  1. 栈的基本操作:
  • 初始化
  • 进栈
  • 出栈
  • 获取栈顶元素
  • 判空、判满
    带头结点的链栈基本操作:
#include<stdio.h>

struct Linknode{
    int data;             //数据域
    Linknode *next;       //指针域
}Linknode,*LiStack;   

typedef Linknode *Node;   //结点结构体指针变量
typedef Node List;        //结点结构体头指针变量

//1. 初始化
void InitStack(LiStack &L){   //L为头指针
    L = new Linknode; 
    L->next = NULL;
}

//2.判栈空
bool isEmpty(LiStack &L){
    if(L->next == NULL){
        return true;
    }
    else
        return false;
}

//3. 进栈(:链栈基本上不会出现栈满的情况)
void pushStack(LiStack &L, int x){
    Linknode s;          //创建存储新元素的结点
    s = new Linknode;
    s->data = x;

    //头插法
    s->next = L->next;
    L->next = s;
}

//4.出栈
bool popStack(LiStack &L, int &x){
    Linknode s;
    if(L->next == NULL) //栈空不能出栈
        return false;
    
    s = L->next;
    x = s->data;
    L->next = L->next->next;
    delete(s);

    return true;
}



不带头结点的链栈基本操作:

#include<stdio.h>

struct Linknode{
    int data;             //数据域
    Linknode *next;       //指针域
}Linknode,*LiStack;   

typedef Linknode *Node;   //结点结构体指针变量
typedef Node List;        //结点结构体头指针变量

//1.初始化 
void initStack(LiStack &L){
    L=NULL;
}

//2.判栈空
bool isEmpty(LiStack &L){
    if(L == NULL)
        return true;
    else
        teturn false;
}

//3.进栈
void pushStack(LiStack &L, int x){
    Linknode s;          //创建存储新元素的结点
    s = new Linknode;

    s->next = L;
    L = s;
}

//4.出栈
bool popStack(LiStack &L, int &x){
    Linknode s; 
    if(L = NULL)     //栈空不出栈
        return false;

    s = L;
    x = s->data;
    L = L->next;
    delete(s);
    
    return true;
}

3.2队列(Queue)

3.2.1队列的基本概念

1.定义:队列(Queue)简称队,是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。
2.特点

  • 队列是操作受限的线性表,只允许在一端进行插入 (入队),另一端进行删除 (出队)
  • 操作特性:先进先出 FIFO
  • 队头:允许删除的一端
  • 队尾:允许插入的一端
  • 空队列:不含任何元素的空表
    3.队列的基本操作
    “创建&销毁”
  • InitQueue(&Q): 初始化队列,构造一个空列表Q
  • DestroyQueue(&Q): 销毁队列,并释放队列Q所占用的内存空间
    “增&删”
  • EnQueue(&Q, x): 入队,若队列Q未满,将x加入,使之成为新的队尾
  • DeQueue(&Q, &x): 出队,若队列Q非空,删除队头元素,并用x返回
    “查&其他”
  • GetHead(Q,&x): 读队头元素,若队列Q非空,则将队头元素赋值给x
  • QueueEmpty(Q): 判队列空,若队列Q为空,则返回

3.2.2队列的顺序存储结构

  • 队头指针:指向队头元素
  • 队尾指针:指向队尾元素的下一个位置
  1. 队列存储的基本操作
//队列的顺序存储类型
# define MaxSize 10;     //定义队列中元素的最大个数
typedef struct{
    ElemType data[MaxSize];   //用静态数组存放队列元素
                              //连续的存储空间,大小为——MaxSize*sizeof(ElemType)
    int front, rear;          //队头指针和队尾指针
}SqQueue;

//初始化队列
void InitQueue(SqQueue &Q){
    //初始化时,队头、队尾指针指向0
    Q.rear = Q.front = 0;
}

void test{
    SqQueue Q;                //声明一个队列
    InitQueue(Q);
    //...
}

// 判空
bool QueueEmpty(SqQueue 0){
    if(Q.rear == Q.front)    //判空条件后
        return true;
    else 
        return false;
}

  1. 循环队列
    定义:将循环队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。
    基本操作:
a%b == a除以b的余数

初始:Q.front = Q.rear = 0;

队首指针进1:Q.front = (Q.front + 1) % MaxSize

队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize —— 队尾指针后移,当移到最后一个后,下次移动会到第一个位置

队列长度:(Q.rear + MaxSize - Q.front) % MaxSize

区分队空还是队满的情况:
方案一: 牺牲一个单元来区分队空和队满
队尾指针的再下一个位置就是队头,即 (Q.rear+1)%MaxSize == Q.front

  • 循环队列——入队:只能从队尾插入(判满使用方案一)
bool EnQueue(SqQueue &Q, ElemType x){
    if((Q.rear+1)%MaxSize == Q.front)        //队满
        return false;
    Q.data[Q.rear] = x;                      //将x插入队尾
    Q.rear = (Q.rear + 1) % MaxSize;         //队尾指针加1取模

    return true;
}


  • 循环队列——出队:只能让队头元素出队
//出队,删除一个队头元素,用x返回
bool DeQueue(SqQueue &Q, ElemType &x){
    if(Q.rear == Q.front)              //队空报错
        return false;  

    x = Q.data[Q.front];
    Q.front = (Q.front + 1) % MaxSize; //队头指针后移动

    return true;
}

  • 循环队列——获得队头元素
bool GetHead(SqQueue &Q, ElemType &x){
    if(Q.rear == Q.front)              //队空报错
        return false;  

    x = Q.data[Q.front];
    return true;
}

方案二: 不牺牲存储空间,设置size
定义一个变量 size用于记录队列此时记录了几个数据元素,初始化 size = 0,进队成功 size++,出队成功size--,根据size的值判断队满与队空

队满条件:size == MaxSize

队空条件:size == 0

# define MaxSize 10;     
typedef struct{
    ElemType data[MaxSize];   
    int front, rear;        
    int size;               //队列当前长度
}SqQueue;

//初始化队列
void InitQueue(SqQueue &Q){
    Q.rear = Q.front = 0;
    size = 0;
}

方案三: 不牺牲存储空间,设置tag
定义一个变量 tagtag = 0 --最近进行的是删除操作;tag = 1 --最近进行的是插入操作;

每次删除操作成功时,都令tag = 0;只有删除操作,才可能导致队空;
每次插入操作成功时,都令tag = 1;只有插入操作,才可能导致队满;
队满条件:Q.front == Q.rear && tag == 1

队空条件:Q.front == Q.rear && tag == 0

# define MaxSize 10;     
typedef struct{
    ElemType data[MaxSize];   
    int front, rear;        
    int tag;               //最近进行的是删除or插入
}SqQueue;


3.2.3队列的链式存储结构

1.定义:队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。
链队列:用链表表示的队列,是限制仅在表头删除和表尾插入的单链表。
队列的链式存储类型可描述为:

typedef struct LinkNode{      //链式队列结点
    ElemType data;
    struct LinkNode *next;
}

typedef struct{               //链式队列
    LinkNode *front, *rear;   //队列的队头和队尾指针
}LinkQueue;

2.链式队列的基本操作——带头结点

  • 初始化 & 判空
void InitQueue(LinkQueue &Q){
    //初始化时,front、rear都指向头结点
    Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
    Q.front -> next = NULL;
}

//判断队列是否为空
bool IsEmpty(LinkQueue Q){
    if(Q.front == Q.rear)     //也可用 Q.front -> next == NULL
        return true;
    else
        return false;
}

  • 入队操作
//新元素入队 (表尾进行)
void EnQueue(LinkQueue &Q, ElemType x){
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode)); //申请一个新结点
    s->data = x;
    s->next = NULL;     //s作为最后一个结点,指针域指向NULL
    Q.rear->next = s;   //新结点插入到当前的rear之后
    Q.rear = s;         //表尾指针指向新的表尾
}

  • 出队操作
//队头元素出队
bool DeQueue(LinkQueue &Q, ElemType &x){
    if(Q.front == Q.rear)
        return false;                    //空队
    
    LinkNode *p = Q.front->next;         //p指针指向即将删除的结点 (头结点所指向的结点)
    x = p->data;
    Q.front->next = p->next;             //修改头结点的next指针
    if(Q.rear == p)                      //此次是最后一个结点出队
        Q.rear = Q.front;                //修改rear指针
    free(p);                             //释放结点空间

    return true;
}

  • 队列满的条件
    顺序存储:预分配存储空间

链式存储:一般不会队满,除非内存不足

  • 计算链队长度 (遍历链队)
    设置一个int length 记录链式队列长度

  • 初始化 & 判空

  • 入队操作

//新元素入队 (表尾进行)
void EnQueue(LinkQueue &Q, ElemType x){
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode)); //申请一个新结点
    s->data = x;
    s->next = NULL;

    //第一个元素入队时需要特别处理
    if(Q.front = NULL){            //在空队列中插入第一个元素
        Q.front = s;               //修改队头队尾指针
        Q.rear = s;
    }else{
        Q.rear->next = s;           //新结点插入到rear结点之后
        Q.rear = s;                 //修改rear指针指向新的表尾结点
    }
}

3.2.4双端队列

1.定义:双端队列是指允许两端都可以进行入队和出队操作的队列

  • 双端队列允许从两端插入、两端删除的线性表;
  • 如果只使用其中一端的插入、删除操作,则等同于栈;
  • 输入受限的双端队列:允许一端插入,两端删除的线性表;
  • 输出受限的双端队列:允许两端插入,一端删除的线性表;

下图是双端队列的示意图:
在这里插入图片描述

3.2.5循环队列

利用一组地址连续的存储单元依次存放队列中的数据元素。因为队头和队尾的位置是变化的。所以:设头、尾指针。

求循环队列的长度:
在这里插入图片描述

3.3栈的应用

3.3.1栈在括号匹配中的应用

用栈实现括号匹配

  • ((())) 最后出现的左括号最先被匹配 (栈的特性—LIFO);

  • 遇到左括号就入栈;

  • 遇到右括号,就“消耗”一个左括号 (出栈);
    匹配失败情况:

  • 扫描到右括号且栈空,则该右括号单身;

  • 扫描完所有括号后,栈非空,则该左括号单身;

  • 左右括号不匹配;

代码:

#define MaxSize 10   

typedef struct{
    char data[MaxSize];
    int top;
} SqStack;

//初始化栈
InitStack(SqStack &S)

//判断栈是否为空
bool StackEmpty(SqStack &S)

//新元素入栈
bool Push(SqStack &S, char x)

//栈顶元素出栈,用x返回
bool Pop(SqStack &S, char &x)



bool bracketCheck(char str[], int length){
    SqStack S;      //声明
    InitStack(S);   //初始化栈

    for(int i=0; i<length; i++){
        if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
            Push(S, str[i]);       //扫描到左括号,入栈
        }else{
            if(StackEmpty(S))      //扫描到右括号,且当前栈空
                return false;      //匹配失败
            
            char topElem;          //存储栈顶元素
            Pop(S, topElem);       //栈顶元素出栈

            if(str[i] == ')' && topElem != '(' )
                return false;

            if(str[i] == ']' && topElem != '[' )
                return false;

            if(str[i] == '}' && topElem != '{' )
                return false;       
        }
    }

    StackEmpty(S);                //栈空说明匹配成功
}


3.3.2栈在表达式求值中的应用

1. 中缀表达式 (需要界限符)
运算符在两个操作数中间:

① a + b
② a + b - c
③ a + b - c*d
④ ((15 ÷ (7-(1+1)))×3)-(2+(1+1))
⑤ A + B × (C - D) - E ÷ F

2. 后缀表达式 (逆波兰表达式)
运算符在两个操作数后面:

① a b +
② ab+ c - / a bc- +
③ ab+ cd* -15 7 1 1 + - ÷ 3 × 2 1 1 + + -
⑤ A B C D - × + E F ÷ - (机算结果)
  A B C D - × E F ÷ - + (不选择)

中缀表达式转后缀表达式-手算
步骤1: 确定中缀表达式中各个运算符的运算顺序

步骤2: 选择下一个运算符,按照[左操作数 右操作数 运算符]的方式组合成一个新的操作数

步骤3: 如果还有运算符没被处理,继续步骤2

“左优先”原则: 只要左边的运算符能先计算,就优先算左边的 (保证运算顺序唯一);

中缀:A + B - C * D / E + F
       ①   ④   ②   ③   ⑤     
后缀:A B + C D * E / - F +

重点:中缀表达式转后缀表达式-机算
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素,直到末尾。可能遇到三种情况:

遇到操作数: 直接加入后缀表达式。
遇到界限符: 遇到 ‘(’ 直接入栈; 遇到 ‘)’ 则依次弹出栈内运算符并加入后缀表达式,直到弹出 ‘(’ 为止。注意: '(' 不加入后缀表达式。
遇到运算符: 依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到 ‘(’ 或栈空则停止。之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

后缀表达式的计算—手算:
从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应的运算,合体为一个操作数;

注意: 两个操作数的左右顺序

重点:后缀表达式的计算—机算
用栈实现后缀表达式的计算(栈用来存放当前暂时不能确定运算次序的操作数)

步骤1: 从左往后扫描下一个元素,直到处理完所有元素;

步骤2: 若扫描到操作数,则压入栈,并回到步骤1;否则执行步骤3;

步骤3: 若扫描到运算符,则弹出两个栈顶元素,执行相应的运算,运算结果压回栈顶,回到步骤1;

注意: 先出栈的是“右操作数”

3.前缀表达式 (波兰表达式)
运算符在两个操作数前面:

+ a b
② - +ab  c
③ - +ab *cd

中缀表达式转前缀表达式—手算
步骤1: 确定中缀表达式中各个运算符的运算顺序

步骤2: 选择下一个运算符,按照[运算符 左操作数 右操作数]的方式组合成一个新的操作数

步骤3: 如果还有运算符没被处理,就继续执行步骤2

“右优先”原则: 只要右边的运算符能先计算,就优先算右边的;

中缀:A + B * (C - D) - E / F
       ⑤   ③    ②    ④   ①
前缀:+ A - * B - C D / E F

前缀表达式的计算—机算
用栈实现前缀表达式的计算

步骤1: 从右往左扫描下一个元素,直到处理完所有元素;

步骤2: 若扫描到操作数则压入栈,并回到步骤1,否则执行步骤3

步骤3: 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到步骤1;

注意: 先出栈的是“左操作数”

4.中缀表达式的计算(用栈实现)
两个算法的结合: 中缀转后缀 + 后缀表达式的求值

初始化两个栈,操作数栈 和运算符栈

若扫描到操作数,压人操作数栈

若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈 (期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈项元素并执行相应运算,运算结果再压回操作数栈)

3.3.3栈在递归中的应用

函数调用的特点:最后被调用的函数最先执行结束(LIFO)

函数调用时,需要用一个栈存储:

  • 调用返回地址
  • 实参
  • 局部变量

递归调用时,函数调用栈称为 “递归工作栈”:

  • 每进入一层递归,就将递归调用所需信息压入栈顶;
  • 每退出一层递归,就从栈顶弹出相应信息;

**缺点:**太多层递归可能回导致栈溢出;

适合用“递归”算法解决:可以把原始问题转换为属性相同,但规模较小的问题;

3.4特殊矩阵的压缩存储

矩阵定义: 一个由m*n个元素排成的m行(横向)n列(纵向)的表。
矩阵的常规存储:将矩阵描述为一个二维数组。

3.4.1数组的存储结构

1.一维数组

Elemtype a[10];

各数组元素大小相同,物理上连续存放;

起始地址:LOC

数组下标:默认从0开始!

数组元素 a[i] 的存放地址 = LOC + i × sizeof(ElemType)

2.二维数组

Elemtype b[2][4]; //2行4列的二维数组

行优先/列优先存储优点:实现随机存储

起始地址:LOC

M行N列的二维数组 b[M][N] 中,b[i][j]的存储地址:

行优先存储: LOC + (i×N + j) × sizeof(ElemType)
列优先存储:LOC + (j×M + i) × sizeof(ElemType)

3.4.2普通矩阵的存储

二维数组存储:

  • 描述矩阵元素时,行、列号通常从1开始;
  • 描述数组时,通常下标从 0 开始;

3.4.3特殊矩阵的存储

特殊矩阵——压缩存储空间(只存有用的数据)

矩阵的压缩存储:为多个相同的非零元素只分配一个存储空间;对零元素不分配空间。

  1. 对称矩阵(方阵)

在一个n阶方阵A中,若元素满足下述性值:在这里插入图片描述
则称A为对称矩阵。
在这里插入图片描述

  1. 三角矩阵(方阵)
    以主对角线划分,三角矩阵有上(下)三角两种。上(下)三角矩阵的下(上)三角(不含主对角线)中的元素均为常数。在大多数情况下,三角矩阵常数为零。

在这里插入图片描述

  1. 三对角矩阵(方阵)
    在这里插入图片描述
    对角矩阵可按行优先顺序或对角线的顺序,将其压缩存储到一维数组中,且也能找到每个非零元素和向量下标的对应关系。

  2. 稀疏矩阵
    设在mn的矩阵中有t个非零元素,令c=t/(mn),当c<=0.05时称为稀疏矩阵。
    压缩存储原则:存各非零元的值、行列位置和矩阵的行列数。

  • 顺序存储——三元组

在这里插入图片描述

  • 链式存储——十字链表法
    优点:它能够灵活得插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的运算。
    十字链表中结点的结构示意图:
    在这里插入图片描述
    right:用于链接同一行中的下一个非零元素;
    down:用于链接同一列中的下一个非零元素。
    在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【点云抽稀】一种基于均匀分布随机数的点云抽稀算法

文章目录 1. 背景2. 原理3. 实现3.1 定义Utils类3.2 加入预定义宏&#xff0c;确定层级3.3 函数实现 1. 背景 在大数据点云的存储中&#xff0c;常常要进行空间分区&#xff0c;一般的策略是构建四叉树或者八叉树。在构建树的过程中&#xff0c;一个不可避免的点就是点云的快速…

Text, Shapes, Colors 的使用

1. Text 的使用 // 多行文本 // Hello, World! This is the Swiftful Thinking Bootcamp. I am really enjoying this course and learning alot.// .lowercased() 文字小写 .uppercased() 文字大写 capitalized: 以单词形式显示 Text("Hello, World!".capitalized)…

test123

import matplotlib.pyplot as plt import pandas as pd # 从Power BI中获取输入数据 data pd.DataFrame({ Country: [USA, Canada, UK, Germany, France], Population: [328, 38, 66, 83, 67] }) # 使用Matplotlib绘制图表 plt.bar(data[Country], data[Population]…

Sentinel的另外三种流控模式(附代码详细介绍)

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将详细介绍Sentinel的其他三种流控模式&#xff0c;后续文章将详细介绍Sentinel的其他知识。 如果文章有什么需要改进的地方还请大佬不吝赐教&#x1f44f;&#…

轻量应用服务器如何选择之阿里云和腾讯云PK哪个好?

阿里云和腾讯云都有轻量应用服务器&#xff0c;轻量服务器是一种轻量级开箱即用的云服务器&#xff0c;适合小型网站、博客或测试等单机应用&#xff0c;那么问题来了&#xff0c;轻量应用服务器到底是阿里云好还是腾讯云好&#xff1f;阿腾云来详细对比下阿里云轻量应用服务器…

find命令的结果顺序

文章目录 1. 目的2. 准备&#xff1a; 克隆 doxygen 源码3. ubuntu22.04 结果4. ubuntu16.04 结果5. git bash 结果6. 三路比较7. 保持一样的结果&#xff1a; 用自然排序8. References 1. 目的 在研读 doxygen 源码时&#xff0c; 在不同电脑、不同操作系统上使用了 find 命令…

vue2中provide/inject的使用

一般来说&#xff0c;组件之间有以下几种关系&#xff1a; A-B、A-C、B-D、B-E、C-F都是父子关系&#xff0c;B-C、D-E-F是兄弟关系&#xff0c;A-D、A-E、A-F是祖先与孙子关系。 vue2中provide/inject这对选项需要一起使用&#xff0c;以允许一个祖先组件向其所有子孙后代注…

私有化部署即时通讯工具能为企业提升工作沟通效率吗?

在疫情的影响下&#xff0c;越来越多的企业开始采用私有化部署的方式部署即时通讯工具&#xff0c;以提升工作沟通效率。私有化部署的即时通讯工具&#xff0c;能够为企业提供完善的人员管理、安全保障、灵活便捷等优势&#xff0c;帮助企业更好地管理人员、团队协作以及工作沟…

CAMx-Python融合技术与大气污染来源解析方法

详情点击链接&#xff1a;CAMx-Python融合技术应用与大气污染来源解析方法 CAMx模型是一个基于大气化学&#xff0c;针对臭氧、颗粒物和雾霾天气过程的大气污染物计算模型。 1、CAMx模式的区域空气质量模拟配置技术&#xff1b; 2、SMOKE模型的CAMx模式大气排放清单输入准备&…

Kubernetes 二进制部署高可用集群 失败 看报错

概述 openssl证书有问题导致失败&#xff0c;未能解决openssl如何创建私钥&#xff0c;可参考ansible 在私有局域网内完成Kubernetes二进制高可用集群的部署 ETCD Openssl > ca 证书 Haproxy Keepalived Kubernetes 主机规划 序号名字功能VMNET 1备注 1备注 2备注 3 备注…

拉线位移传感器可以用来做的工作

拉线位移传感器可以用来做的工作 拉线位移传感器&#xff0c;是做什么的呢&#xff1f;是测位移的一种传感器&#xff0c;它的使用方式是用拉线测量&#xff0c;所以我们又叫它拉线位移传感器或者拉绳位移传感器。 拉绳位移传感器的应用非常广泛&#xff0c;一般只要精度要求不…

Docker issue failed to solve: rpc error: code = unknown desc

完整错误&#xff1a;failed to solve: rpc error: code Unknown desc failed to solve with frontend dockerfile.v0: failed to create LLB definition: unexpected status code [manifests 8.0-alpine-v3.14-swoole]: 403 Forbidden 解决方案一 重启DockerDesktop或重新启…

JavaScript全解析-继承

继承 ●要知道什么是继承 ●要知道继承的方式有哪些 ●每种的继承方式是如何实现的 什么是继承 ●继承关系出现在构造函数和构造函数之间 ●当构造函数A 的实例使用了 构造函数B 的属性和方法 ●我们就说 构造函数A 继承自 构造函数B ○管 构造函数A 叫做子类 ○管 构造函数B 叫…

MATLAB算法实战应用案例精讲-【数模应用】生存曲线(补充篇)

目录 前言 几个相关概念 生存概率与死亡概率 生存率 生存曲线 事件、生存时间 中位生存时间 生存率的比较 生存数据 风险集 如何读懂KM曲线 应用案例 新药对患者总生存时间的影响-KM曲线 软件操作及结果解读 应用GraphPad Prism制作生存曲线 SPSS绘制生存曲线图 …

gRPC-go参数功能介绍1->WithBlock参数介绍

在gRPC-go框架中&#xff0c;当客户端使用 Dial() 方法连接到gRPC服务器时&#xff0c;可以使用 WithBlock() 选项来阻塞客户端&#xff0c;直到与服务器建立连接成功。 通常情况下&#xff0c;当客户端调用 Dial() 方法时&#xff0c;该方法会立即返回&#xff0c;并在后台异…

使用MinIO文件存储系统【完成图片上传保存】业务逻辑

目录 1&#xff1a;业务流程 2&#xff1a;接口实现 controller层 service层 1&#xff1a;业务流程 步骤一&#xff1a;前端进入上传图片的界面 步骤二&#xff1a;上传图片&#xff0c;请求到后端的媒资管理服务模块 步骤三&#xff1a;媒资管理服务将图片文件存储到m…

STM32---编写呼吸灯串口发送ON开LED,发送OFF关LED或者0X550X440XFF表示开灯,0X550X660XFF表示关灯

编写呼吸灯串口发送ON开LED&#xff0c;发送OFF关LED或者&#xff08;0X550X440XFF表示开灯&#xff0c;0X550X660XFF表示关灯&#xff09;注&#xff1a;包头 0X55 包尾&#xff1a;0XFF 数据&#xff1a;0X44表示开灯 0X66表示关灯 用到了重定向 //printf的重定向 int fpu…

使用SSD会提高游戏性能或FPS吗?

​“我在考虑要不要给电脑换个SSD&#xff0c;现在旧电脑上的HDD快满了&#xff0c;正好我也喜欢打游戏&#xff0c;听说换SSD可以提高电脑性能以及游戏FPS&#xff0c;这是真的吗&#xff1f;如果是真的&#xff0c;那我怎么样可以把旧硬盘上的数据迁移到新硬盘呢&#xff1f;…

【Web3.0大势所趋】下一代互联网的未来

前言 Web3.0 是一个越来越受到关注的话题&#xff0c;它被认为将会带来天翻地覆的变化。本文我们一起来谈谈 Web3.0 的概念、特点和优势&#xff0c;并探讨它为什么如此重要和具有革命性的。 文章目录 前言Web3.0是什么区块链技术智能合约总结 Web3.0是什么 Web3.0: 是下一代互…

达梦数据库的安装DM8

文章目录 一、达梦数据库的安装1、环境需求2、达梦的官方安装文档3、达梦数据库的安装包下载3.1、DM8的下载3.2、DM7的下载 4、开始操作4.1、使用xftp传dm8到虚拟机里4.2、下载依赖包4.3、拷贝dm8的iso文件到指定目录&#xff0c;并挂载4.4、创建DM安装用户和安装用户组并初始化…