【数据结构】数据结构与算法基础 课程笔记 第三章 栈和队列

栈和队列的定义和特点

栈(Stack)的相关概念

  • 定义:限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作)。

  • 逻辑结构:与线性表相同,仍为一对一关系。

  • 存储结构:包括顺序栈和链栈,但顺序栈更常见。

  • 运算规则:只能在栈顶运算,访问结点是依照后进先出(Last In First Out , LIFO)的原则;栈与一般线性表的区别就在于此,一般线性表的运算规则是随机存取。

  • 应用场景:递归调用,函数调用,表达式求值,匹配括号。

  • 结构、操作图示:

队列(Queue)的相关概念

  • 定义:只能在表的一端进行插入运算,在表的另一段进行删除运算的线性表(尾插头删)。

  • 逻辑结构:与线性表相同,为一对一关系。

  • 存储结构:包括顺序队和链队,以循环顺序队列更常见。

  • 运算规则:只能在队首出队,在队尾入队,依照先进先出(First In First Out , FIFO)的原则。

  • 图示:

栈的表示和操作的实现

栈的抽象数据类型的类型定义

顺序栈

顺序栈的存储方式

  • 顺序栈和顺序表的存储方式完全相同,是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素(栈底一般在低地址端),

  • 另设top指针和base指针,分别指示栈顶元素和栈底元素在顺序栈中的位置;但是为了操作方便,通常top指示的是栈顶元素之上的下标地址。

  • 另外。用stacksize表示栈可使用的最大容量。

  • 图示:

  • 空栈:base==top

  • 栈满:top-base==stacksize

  • 栈满时的处理方法:

  1. 报错,返回操作系统。

  1. 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈。

  • 使用数组作为顺序栈存储方式的特点:简单方便,但容易产生溢出;

  1. 上溢(overflow):栈已经满,又要压入元素;

  1. 下溢(underflow):栈已经空,还要弹出元素。

  1. 注:上溢是一种错误,使问题的处理无法进行;而下溢一般认为是一种结束条件,即问题处理结束。

顺序栈的表示和算法实现

顺序栈的表示
#define MAXSIZE 100
typedef struct{
    SElemType *base;//栈底指针
    SElemType *top;//栈顶指针
    int stacksize;//栈可用最大容量
}SqStack;
顺序栈的初始化
Status InitStack(SqStack &S){//构造一个空栈
    S.base=new SElemType[MAXSIZE];//开辟出一个数组空间,base作为数组的基地址
    //或者如下:
    //S.base=(SElemType*)malloc(MAXSIZE*sizeof(SElemType));
    if(!S.base) exit(OVERFLOW);//若开辟失败,则S.base不存在,即为空,所以退出并返回OVERFLOW
    S.top=S.base;//初始化时,栈顶指针等于栈底指针
    S.stacksize=MAXSIZE;
    return OK;
}
判断顺序栈是否为空
Status StackEmpty(SqStack S){
    if(S.top==S.base)
        return TRUE;
    else
        return FALSE;
}
求顺序栈的长度
int StackLength(SqStack S){
    return S.top-S.base;
}
清空顺序栈
Status ClearStack(SqStack &S){
    if(S.base) S.top=S.base;
    return OK;
}
销毁顺序栈
Status DestroyStack(SqStack &S){
    if(S.base){
        delete S.base;
        S.stacksize=0;
        S.base=S.top=NULL;
    }
    return OK;
}
顺序栈的入栈
Status Push(SqStack &S,SElemType e){
    if(S.top-S.base==S.stacksize)//栈满
        return ERROR;
    *S.top++=e;
    return OK;
}
顺序栈的出栈
Status Pop(SqStack &S,SElemType &e){
    if(S.top==S.base)
        return ERROR;
    e=*--S.top;
    return OK;
}

链栈的表示和算法实现

链栈的表示
typedef struct StackNode{
    SElemType data;
    struct StackNode *next;
    }StackNode,*LinkStack;//定义一个结构类型StackNode作为栈的结点类型,再用这个类型定义一个指向这种类型的指针类型LinkStack
LinkStack S;//定义一个结点指针S,表示一个栈
链栈的特点
  • 链栈的头指针就是栈顶;

  • 不需要头结点,这样操作起来比较方便;

  • 栈顶元素是首元结点存储的元素,栈底元素是尾结点存储的元素;

  • 基本不存在栈满的情况,需要空间可以直接从内存里找;

  • 空栈相当于头指针指向空(NULL);

  • 插入和删除仅在栈顶处执行。

链栈的初始化
void InitStack(LinkStack &S){
    S=NULL;//创造一个空栈,栈顶指针置空
    return OK;
}
判断链栈是否为空
Status StackEmpty(LinkStack S){
    if(S==NULL) return TRUE;
    else return FALSE;
}
链栈的入栈
Status Push(LinkStack &S,SElemType e){
    p=new StackNode;//生成新结点p
    p->data=e;//将新结点的数据域置为e
    p->next=S;//将新结点插入栈顶
    S=p;//修改栈顶指针
    return OK;
}
链栈的出栈
Status Pop(LinkStack &S,SElemType &e){
    if(S==NULL) return ERROR;
    e=S->data;
    p=S;//预先定义一个指针p指向要弹出的链栈结点
    S=S->next;
    delete p;
    return OK;
}
取栈顶元素
SElemType GetTop(LinkStack S){
    if(S!=NULL)
    return S->data;
}

队列的表示和操作的实现

队列的抽象数据类型定义

队列的顺序表示——用一维数组base[MAXQSIZE]

#define MAXQSIZE 100//最大队列长度
typedef struct{
    QElemType *base;//用base数组来表示队列
    int front;//头指针,不是指针型而是整型,因为存放的是下标,队头元素的下标
    int rear;//尾指针,队列中可以存放(队尾元素下标+1)个元素
}SqQueue;//队列的数据中没有存储队长的部分,而是用到两个“指针”来表示队头元素和队尾元素的下标
顺序队的溢出
  • 当rear=MAXQSIZE时,发生溢出(一般来说rear为MAXSIZE-1时顺序队就满了);

  • 当front=0 , rear=MAXQSIZE时,若再入队,则为真溢出

  • 当front≠0 , rear=MAXQSIZE时,若再入队,则为假溢出(因为队头还有空间);

为规避溢出的问题,将顺序队改为循环顺序队,简称循环队列。

循环队列的表示和算法实现

循环队列的类型定义

#define MAXQSIZE 100
typedef struct{
    QElemType *base;//用指针代替数组,这样可以动态分配存储空间,不受限制
    int front;//头指针,若队列不空,指向队列头元素
    int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;

循环队列的初始化

Status InitQueue(SqQueue &Q){
    Q.base=new QElemType[MAXQSIZE];//分配空间
    if(!Q.base) exit(OVERFLOW);
    Q.front=Q.rear=0;//头尾指针置为0,队列为空
    return OK;
}

求循环队列的长度

int QueueLength(SqQueue Q){
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;//因为循环后会出现尾指针比头指针小的情况,所以加上队长再求余
}

循环队列入队

Status EnQueue(SqQueue &Q,QElemType e){
    if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
    Q.base[Q.rear]=e;
    Q.rear=(Q.rear+1)%MAXQSIZE;
    return OK;
}

循环队列出队

Status DeQueue(SqQueue &Q,QElemType &e){
    if(Q.front==Q.rear) return ERROR;//队空
    e=Q.base[Q.front];//保存队头元素
    Q.front=(Q.front+1)%MAXQSIZE;//队头指针+1
    return OK;
}

取循环队列的队头元素

SElemType GetHead(SqQueue Q){
    if(Q.front!=Q.rear)//队列不为空
        return Q.base[Q.front];
}

链队的表示和算法实现

使用原因:用户无法估计所用队列的长度。

链队的类型定义

#define MAXQSIZE 100
typedef struct Qnode{
    QElemType data;
    struct Qnode *next;
}Qnode,*QueuePtr;
typedef struct{
    QueuePtr front;//队头指针,指向头结点
    QueuePtr rear;//队尾指针
}LinkQueue;
LinkQueue Q;

链队列初始化

Status InitQueue(LinkQueue &Q){
    Q.front=Qrear=(QueuePtr)malloc(sizeof(Qnode));
    if(!Q.front) exit(OVERFLOW);
    Q.front->next=NULL;
    return OK;
}

销毁链队列

Status DestroyQueue(LinkQueue &Q){
    while(Q.front){//从队头结点开始,依次释放所有结点
        p=Q.front->next;
        free(Q.front);
        Q.front=p;
    }
    return OK;
}

链队列的入队

Status EnQueue(LinkQueue &Q,QElemType e){
    p=(QueuePtr)malloc(sizeof(Qnode));
    if(!p) exit(OVERFLOW);
    p->data=e;
    p->next=NULL;
    Q.rear->next=p;
    Q.rear=p;
    return OK;
}

链队列的出队

Status DeQueue(LinkQueue &Q,QElemTupe &e){
    if(Q.front==Q.rear) return ERROR;
    p=Q.front->next;//设置一个指针p指向要出队的元素
    e=p->data;//因为要返回,所以将要出队的元素赋值给e
    Q.front->next=p->next;//将头指针同后面的结点相连
    if(Q.rear==p) Q.rear=Q.front;//如果出队的是尾结点,那么要让尾指针和头指针都指向头结点
    delete p;
    return OK;
}

求链队列的队头元素

Status GetHead(LinkQueue Q,QElemType &e){
    if(Q.front==Q.rear) return ERROR;
    e=Q.front->next->data;
    return OK;
}

习题

课本习题

  • 设有一个递归算法如下:

int fact ( int n )

{ //n 大于等于0

if(n<=0) return 1;

else return n*fact(n-1); }

则计算fact(n) 需要调用该函数的次数为( )。

A. n+1 B. n-1 C.n D.n+2

答案:A
解析:观察fact函数可以发现这个函数是用来计算n的阶乘,即为n*(n-1)…*1
所以将执行n+1次
特殊值法。n = 0 ,仅调用一次fact(n) 函数,所以执行n+1次

期末题库

3

若一个栈的输入序列为1,2,3,…,n,输出序列的第一个元素是i,则第j个输出元素是( )。

A. i-j-1

B. i-j

C. j-i+1

D. 不确定的

答案:D
解析:i和j无关,纯纯的文字游戏。

初始化一个空间大小为5的顺序栈Ss后,Ss->top的值( )。

A. 是0

B. 不定

C. 不再改变

D. 动态变化

答案:A

循环队列占用的空间( )。

A. 必须连续

B. 可以不连续

C. 不能连续

D. 不必连续

答案:A

初始化一个空间大小为5的循环队列Sq后,Sq->front和Sq->rear的值都是( )。

A. 0

B. 不定

C. 不再改变

D. 1

答案:A

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值