866数据结构笔记 - 第三章 栈和队列

 湖大计学考研系列文章目录


目录

重点内容

一、基本概念

二、栈

1.顺序栈

2.定义

3.两种实现

4.共享栈

5.链栈

三、队列

1.顺序队列

2.循环队列

3.链式队列

4.双端队列

四、栈和队列应用

1.栈在括号匹配中的应用

2.队列在层次遍历中的应用 

五、特殊矩阵的压缩 

1.一维数组

2.二维数组

3.特殊矩阵压缩注意点

六、栈和队列输出序列题

七、特殊矩阵压缩题

参考(具体细节见原文)


重点内容

        22年866真题:3个选择+1个简答,共计16分

  1. 栈和队列能在哪进行插入和删除?
  2. 栈和队列的特点?
  3. 顺序栈的两种定义以及操作(入栈、出栈、获取栈顶、判断栈空、栈满)
  4. 共享栈的栈满条件?
  5. 循环队列队满和元素个数公式?
  6. N个元素进栈,出栈个数?
  7. 栈和队列顺序题目?(必考)
  8. 如何将队列逆置?
  9. 括号匹配思想和代码?
  10. 数组和特殊矩阵(对称、三角、三对角)存储注意事项?(优先带值法)

一、基本概念

  1. 栈:操作受限的线性表,只能在栈顶进行插入和删除。
  2. 队列:操作受限的线性表,只能在队尾插入,队头删除。
  3. 特性:
  • 栈:后进先出/先进后出(LIFO)
  • 队列:先进先出(FIFO)

二、栈

1.顺序栈

        用静态数组实现,并且需要记录栈顶指针。

2.定义

#define MaxSize 10         //定义栈中元素的最大个数
typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top;                      //栈顶元素
}SqStack;

3.两种实现

1. 初始化 top=-1(最常用,top指向栈顶元素)

  • 入栈:S.data[++S.top] = x
  • 出栈:x = S.data[S.top--]
  • 获取栈顶元素:x = S.data[S.top]
  • 判断栈空:S.top == -1
  • 判断栈满:S.top == MaxSize - 1

2. 初始化 top=0(top指向栈顶后一位置)

  • 入栈:S.data[S.top++] = x
  • 出栈:x = S.data[--S.top]
  • 获取栈顶元素:x = S.data[--S.top]
  • 判断栈空:S.top == 0
  • 判断栈满:S.top == MaxSize 

4.共享栈

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

  • 0号栈指针初始化:top=-1
  • 1号栈指针初始化:top=MaxSize
  • 判断栈满:top1-top0=1或者top0+1=top1

5.链栈

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

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

三、队列

1.顺序队列

# define MaxSize 10;     //定义队列中元素的最大个数
typedef struct{
    ElemType data[MaxSize];   //用静态数组存放队列元素
                              //连续的存储空间,大小为——MaxSize*sizeof(ElemType)
    int front, rear;          //队头指针和队尾指针
}SqQueue;

2.循环队列

        将循环队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。

初始: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队空:Q.rear==Q.front
  • 增加size变量,记录队列长度:队满:Q.size==Maxsize队空:Q.size==0
  • 增加tag=0/1,标记出队入队 队满:Q.front == Q.rear && tag == 1队空:Q.front == Q.rear && tag == 0

3.链式队列

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

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

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

4.双端队列

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

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

四、栈和队列应用

1.栈在括号匹配中的应用

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

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

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

  • 左右括号不匹配

#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);                //栈空说明匹配成功
}


2.队列在层次遍历中的应用 

  • 初始化一个辅助队列
  • 根节点入队
  • 若队列非空,则队头结点出队,访问该结点,依次将其左、右孩子插入队尾(如果有的话)
  • 重复以上操作直至队列为空
//二叉树的结点(链式存储)
typedef struct BiTnode{
   ElemType data;          
   struct BiTNode *lchild, *rchild; 
}BiTNode, *BiTree;

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

typedef struct{
   LinkNode *front, *rear;  
}LinkQueue;

//层序遍历
void LevelOrder(BiTree T){
   LinkQueue Q;
   InitQueue (Q);          //初始化辅助队列
   BiTree p;
   EnQueue(Q,T);           //将根节点入队
   while(!isEmpty(Q)){     //队列不空则循环
      DeQueue(Q,p);        //队头结点出队
      visit(p);            //访问出队结点
      if(p->lchild != NULL)
         EnQueue(Q,p->lchild);   //左孩子入队
      if(p->rchild != NULL)
         EnQueue(Q,p->rchild);   //右孩子入队
   }
}

五、特殊矩阵的压缩 

1.一维数组

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

2.二维数组

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

3.特殊矩阵压缩注意点

  1. 存储上三角还是下三角
  2. 行优先还是列优先
  3. 矩阵元素下标从0还是1开始
  4. 数组下标从0还是1开始

六、栈和队列输出序列题

        n个不同元素进栈,出栈不同排列个数:

         一个栈的入栈序列是a,b,c,d,e则栈的不可能的输出序列是:()

        A edcbd          B decba          C dceab          D abcde

第一次出栈d,说明什么?说明a,b,c一定早已入栈(入栈顺序决定的)。那么在出栈d以后,a,b,c的出栈顺序一定是c,b,a,而不用理会中间穿插着出栈了d后面的字符(因为可以再入栈,再出栈嘛)。所以立即选中C,不用犹豫,理由简单:d出栈了,abc一定已经入栈,那么abc只能以cba的顺序出栈,C不符合

七、特殊矩阵压缩题

        有一个n×n的对称矩阵A,将其下三角部分换行存放在一维数组B中,而A[0][0]存放产B0l中,则第i+1行的对角元素A[i][i]存放于B中的()处。

A. (i+3)i/2        B. (i+1)i/2        C.(2n-i+1)/2        D.(2n-i-1)i/2

  • 第0行到第i-1行一共:1+2+3+.......+i = i(i+1)/2
  • 第i行有i+1个元素
  • 总共:i(i+1)/2+i+1-1=i(i+3)/2(-1是因为B从0开始)
  • 当然,本题可以使用特殊值法,i=1时,存放在B[2],只有A符合。

参考(具体细节见原文)

参考书目

  1. 王道:《数据结构》
  2. 湖大本科: 《数据结构与算法分析( C++版)(第三版)》Clifford A. Shaffer 著,张铭、刘晓丹等译。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前世忘语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值