栈与队列及其应用C语言实现(数据结构复习最全笔记)(期末复习最新版)

一.栈的基本概念 

(1)栈是限定仅在表尾进行插入和删除操作的线性表。所谓的表尾是指栈顶,而不是栈底。

(2)栈是后进先出的线性表。

(3)把允许插入和删除的一端称为栈顶,另一端称为栈底。

(4)不含任何元素的栈称为空栈。 判定条件为top等于-1。

(5)栈是一个线性表,栈元素具有线性关系。

很明显不是的。

二.栈的表示与实现(顺序栈)

1.栈的顺序结构定义

说一下我写的代码和课本上是一样的,和图片里的有所不同,本质都是一样的,ppt的代码我写在了后头

typedef struct
{
    SElemType *base;//栈底指针
    SElemType *top;//栈顶指针
    int stacksize;//当前最大容量
} SqStack;

2.栈的初始化(构建空栈)

Status InitStack(SqStack &S)
{
    S.base=(SElemType *)malloc(sizeof(SElemType)*STACK_INIT_SIZE);//申请空间
    if(!S.base)
        exit(OVERFLOW);
    S.top=S.base;//头尾指针相等,代表空栈
    S.stacksize=STACK_INIT_SIZE;//当前容量为初始最大容量
    return OK;
}

 3.栈的插入操作

Status Push(SqStack &S,SElemType e)
{
    if(S.top-S.base>=S.stacksize)//空间不够的话要考虑扩容
    {
        S.base=(SElemType*)malloc(sizeof(SElemType)*(S.stacksize+STACKINCREMENT));
        if(!S.base)
            exit(OVERFLOW);
        S.top=S.base+S.stacksize;
        S.stacksize+=STACKINCREMENT;
    }
    //把e插入栈顶
    *S.top++=e;//相当于S.top = e;top++;
    return OK;

}

4.栈的删除操作

注意插入删除操作均不涉及循环操作,所以时间复杂度均为O(1) 

Status Pop(SqStack &S,SElemType &e)
{
    if(S.top==S.base)
        return ERROR;
    e = *S.top--;
    return OK;
}

5.返回栈顶元素

Status GetTop(SqStack &S,SElemType &e)
{
    if(S.base==S.top)
        return ERROR;
    e=*(S.top-1);//当栈不空时,栈顶指针总是指在栈顶元素的下一个位置
    return OK;
}

6.栈的判空

bool JudgeEmpty(SqStack &S)
{
    if(S.top == S.base)
        return true;
    else
        return false;
}

 此外给出ppt版的代码实现:

typedef int Position;
struct SNode {
    ElementType *Data; /* 存储元素的数组 */
    Position Top;      /* 栈顶指针 */
    int MaxSize;       /* 堆栈最大容量 */
};
typedef struct SNode *Stack;
 
Stack CreateStack( int MaxSize )
{
    Stack S = (Stack)malloc(sizeof(struct SNode));
    S->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
    S->Top = -1;
    S->MaxSize = MaxSize;
    return S;
}
 
bool IsFull( Stack S )
{
    return (S->Top == S->MaxSize-1);
}
 
bool Push( Stack S, ElementType X )
{
    if ( IsFull(S) ) {
        printf("堆栈满");
        return false;
    }
    else {
        S->Data[++(S->Top)] = X;
        return true;
    }
}
 
bool IsEmpty( Stack S )
{
    return (S->Top == -1);
}
 
ElementType Pop( Stack S )
{
    if ( IsEmpty(S) ) {
        printf("堆栈空");
        return ERROR; /* ERROR是ElementType的特殊值,标志错误 */
    }
    else 
        return ( S->Data[(S->Top)--] );
}

关于两栈共享空间的问题

黄字表示两个堆栈分别为空的位置

注意,共享栈只针对两个相同类型的栈!!!,如果元素不同,这样做会使问题极其复杂 ,一定要注意这个前提

三.链栈的实现(简单介绍)

由于链栈的操作方法和链表相似,在这里就不详细介绍了,但会给出代码

有几点注意

a:链栈的插入删除操作应该都在最左边即头结点进行

即栈顶指针top只能指向表头,以左边为链表头。

(因为这样方便插入删除,而若top指向表尾,则插入可以,但是删除不行,因为这样就找不到他的直接前驱结点了)

b:链栈中既然有了指向栈顶的top指针,就没有头结点这个概念了哦

c:链栈的插入删除时间复杂度都是O(1)(同顺序栈)

注意这里的插入删除操作

a:插入采用的是头插法,删除就是正常删 

b:这里的插入操作不用判满!!!(必须理解!!!)(这是考点哦!!!)

因为这个堆栈是链表实现的,链表是动态分配存储空间的,不存在表满的问题(数组实现的push操作必须判满)

当然删除操作还是要判空的(因为无论是数组还是链表,都有空的情况)

注意push操作是不需要判满的(因为是动态分配的地址空间)

而pop操作是需要判空的

typedef struct SNode *PtrToSNode;
struct SNode {
    ElementType Data;
    PtrToSNode Next;
};
typedef PtrToSNode Stack;
 
Stack CreateStack( ) 
{ /* 构建一个堆栈的头结点,返回该结点指针 */
    Stack S;
 
    S = (Stack)malloc(sizeof(struct SNode));
    S->Next = NULL;
    return S;
}
 
bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,若是返回true;否则返回false */
    return ( S->Next == NULL );
}
 
bool Push( Stack S, ElementType X )
{ /* 将元素X压入堆栈S */
    PtrToSNode TmpCell;
 
    TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
    TmpCell->Data = X;
    TmpCell->Next = S->Next;
    S->Next = TmpCell;
    return true;
}
 
ElementType Pop( Stack S )  
{ /* 删除并返回堆栈S的栈顶元素 */
    PtrToSNode FirstCell;
    ElementType TopElem;
 
    if( IsEmpty(S) ) {
        printf("堆栈空"); 
        return ERROR;
    }
    else {
        FirstCell = S->Next; 
        TopElem = FirstCell->Data;
        S->Next = FirstCell->Next;
        free(FirstCell);
        return TopElem;
    }
}

 补充一个问题:

栈的作用是什么:

四.栈的应用举例

1.数值转换

所以这个转换过程符合栈的原理

这里有一道例题,我写的应该算很详细了,看懂的话这块应该就ok了

蓝桥杯 基础练习 十进制转十六进制 (练习栈方法)_markconca的博客的博客-CSDN博客_十进制转十六进制栈

关于其他进制转换的原理,想了解的话大家也可以看这个

https://jingyan.baidu.com/article/495ba84109665338b30ede98.html

2.括号匹配检验

基本原理:

思路:1.在算法中设置一个栈

2.每读入一个括号,若是右括号,则要看是否匹配。当栈不空时,如果匹配,则栈顶元素出栈,如果不匹配(即不合法),说明缺少右括号,输出左符号-?(因为如果是右括号,说明之前已经读入左括号,但是两个括号不匹配)。当栈为空时,说明缺少左括号,输出?-右括号。若是左括号,则压入栈中。

3.对于/*符号,可用“<”代替,读到/时记得先i++;

4.算法的开始于结束,栈都应该为空
举两个例题,会了应该就差不多了

例题一:习题3.8 符号配对 (20 分)

pta原题链接:PTA | 程序设计类实验辅助教学平台

该题答案与解析第三章栈作业题2-栈及其应用-计算机17级 7-2 符号配对 (20 分)_markconca的博客的博客-CSDN博客

例题二:练习题 10:括号画家

该题解析与答案:第三章 栈与队列 练习题 10:括号画家_markconca的博客的博客-CSDN博客

3.行编辑程序

这里还没仔细研究,思路日后补充

4.迷宫求解(回溯算法)

 

这里还没仔细研究,思路日后补充

具体的例题日后补充

5.表达式转换(非常重要,也比较难)

注意左括号一旦入栈优先级要降为最低 !!!

思路:

从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。

1.运算数:直接输出

2.左括号:压入栈中

3.右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出)

4.运算符:

若优先级大于栈顶运算符时,则把它压栈;

若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈

5.若各对象处理完毕,则把堆栈中存留的运算符一并输出

这里会这道例题就可以了,不太好做

pta原题链接:习题3.11 表达式转换 (25 分)PTA | 程序设计类实验辅助教学平台

该题超详细答案与解析:(一道老坑爹的题)第三章栈作业题2-栈及其应用-计算机17级 7-1 表达式转换 (25 分)_markconca的博客的博客-CSDN博客

6.其他应用:

a:函数调用及递归实现

b:图的深度优先搜索

补充几道例题哦(详解在下面)

二.链队列的表示与实现

一定注意,不管是顺序实现还是链表实现,队列的插入都是在队尾,删除都是在队头

 注意下面这个问题与链栈类似哦

因为front是做出队操作,rear是做入队操作 ,所以只能是开头是front,结尾是rear(),你要是倒过来就尴尬了,因为表尾不能做删除操作!!!考点!!!(与链栈完全类似哦)

1.链队列的结构定义

typedef struct QNode
{
    QElemType data;
    struct QNode *next;
} QNode,*QueuePtr;

typedef struct
{
    QueuePtr front;
    QueuePtr rear;
} LinkQueue;

2.链队列的初始化(构建空队列)

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

3.链队列的入队操作

Status EnQueue(LinkQueue &Q,QElemType e)
{
    QueuePtr p;
    p = (QueuePtr)malloc(sizeof(QNode));
    if(!p)
        exit(OVERFLOW);
    p->data = e;
        p->next = NULL;
    Q.rear->next = p;//尾部插入
    Q.rear = p;
    return 1;
}

4.链队列的出队操作

Status DeQueue(LinkQueue &Q,QElemType &e)
{
    if(Q.front == Q.rear)
        return 0;
    QueuePtr p = Q.front->next;//指向队头
    e = p->data;
    Q.front->next = p->next;
    //如果队头是队尾,删除后将rear指向头结点
    if(Q.rear == p)
        Q.rear = Q.front;
    free(p);
    return 1;
}

5.链队列的判空操作

bool JudgeEmpty(LinkQueue &Q)
{
    if(Q.front == Q.rear)
        return true;
    else
        return false;
}

 6.链队列的销毁操作

Status DestroyQueue(LinkQueue &Q) {
    while(Q.front) {
        Q.rear = Q.front->next;
        free(Q.front);
        Q.front = Q.rear;
    }
    return true;
}

ppt版队列实现:

typedef struct Node *PtrToNode;
struct Node { /* 队列中的结点 */
    ElementType Data;
    PtrToNode Next;
};
typedef PtrToNode Position;
 
struct QNode {
    Position Front, Rear;  /* 队列的头、尾指针 */
    int MaxSize;           /* 队列最大容量 */
};
typedef struct QNode *Queue;
 
bool IsEmpty( Queue Q )
{
    return ( Q->Front == NULL);
}
 
ElementType DeleteQ( Queue Q )
{
    Position FrontCell; 
    ElementType FrontElem;
     
    if  ( IsEmpty(Q) ) {
        printf("队列空");
        return ERROR;
    }
    else {
        FrontCell = Q->Front;
        if ( Q->Front == Q->Rear ) /* 若队列只有一个元素 */
            Q->Front = Q->Rear = NULL; /* 删除后队列置为空 */
        else                     
            Q->Front = Q->Front->Next;
        FrontElem = FrontCell->Data;
 
        free( FrontCell );  /* 释放被删除结点空间  */
        return  FrontElem;
    }
}

三.循环队列的表示与实现(顺序队列)

初始front=rear=0 

即对顺序存储来说,插入一个元素rear++,删除一个元素front++ 

此时判空和判满都是front==rear,显然出问题了 

 

根本原因:front与rear的差距最多只有n(6)种情况,而队列元素个数总共有n+1(7)种情况,所以仅靠front和rear肯定是表示不了的(考点!!!)

注意:解决方案2用的多

1.循环队列的结构定义

typedef struct{
    QElemType *base;
    int front;
    int rear;
}SqQueue;

2.循环队列的初始化

SqQueue InitQueue(SqQueue &Q)
{
    Q.base=(QElemType *)malloc(MAXQSIZE*sizeof(QElemType));//为队列的数据存储空间分配内存
    if(!Q.base)
       exit(OVERFLOW);
    Q.front = Q.rear = 0;
    return OK;
}

3.循环队列的入队操作

Status EnQueue(SqQueue &Q,QElemType e)
{
    if((Q.rear + 1)% MAXQSIZE == Q.front)//队列满的判断,就是rear+1和front能碰上
        return 0;
    Q.base[Q.rear] = e;//将e赋值给队尾
    Q.rear = (Q.rear + 1) % MAXQSIZE;//rear指针向后移一位,若到最后则转到数组头部
    return 1;
}

4.循环队列的出队操作

Status DeQueue(SqQueue &Q,QElemType &e)
{
    if(Q.front == Q.base)//队列空的判断
        return 0;
    e = Q.base[Q.front];//将队头元素赋值给e
    Q.front = (Q.front + 1) % MAXQSIZE;//front指针向后移一位,若到最后则转到数组头部
    return 1;
}

5.循环队列返回队列元素个数(求队长)

Status QueueLength(SqQueue Q)//返回队列长度
{
    return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}

ppt版代码实现:

typedef int Position;
struct QNode {
    ElementType *Data;     /* 存储元素的数组 */
    Position Front, Rear;  /* 队列的头、尾指针 */
    int MaxSize;           /* 队列最大容量 */
};
typedef struct QNode *Queue;
 
Queue CreateQueue( int MaxSize )
{
    Queue Q = (Queue)malloc(sizeof(struct QNode));
    Q->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
    Q->Front = Q->Rear = 0;
    Q->MaxSize = MaxSize;
    return Q;
}
 
bool IsFull( Queue Q )
{
    return ((Q->Rear+1)%Q->MaxSize == Q->Front);
}
 
bool AddQ( Queue Q, ElementType X )
{
    if ( IsFull(Q) ) {
        printf("队列满");
        return false;
    }
    else {
        Q->Rear = (Q->Rear+1)%Q->MaxSize;
        Q->Data[Q->Rear] = X;
        return true;
    }
}
 
bool IsEmpty( Queue Q )
{
    return (Q->Front == Q->Rear);
}
 
ElementType DeleteQ( Queue Q )
{
    if ( IsEmpty(Q) ) { 
        printf("队列空");
        return ERROR;
    }
    else  {
        Q->Front =(Q->Front+1)%Q->MaxSize;
        return  Q->Data[Q->Front];
    }
}

补充几道例题:(带详解哦)

  • 19
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值