【数据结构】第3章 栈和队列

这是本人根据王道考研数据结构课程整理的笔记,希望对您有帮助。

3.1 栈

3.1.1 栈的基本概念

:栈(stack)是只允许在一端进行插入或删除操作线性表

后进先出, LIFO, Last In First Out
在这里插入图片描述

栈的基本操作

InitStack(&S)初始化栈。构造一个空栈 S S S分配内存空间

DestroyStack(&S)销毁栈。销毁栈,并释放 S S S所占用的内存空间

Push(&S,x)进栈。若栈 S S S未满,则将 x x x加入成为新栈顶。

PoP(&S,&x)出栈。若栈 S S S非空,则弹出栈顶元素,并用 x x x返回。

GetTop(S,&x)读栈顶元素。若栈 S S S非空,则用 x x x返回栈顶元素。

StackEmpty(S):判断一个栈 S S S是否为空。若 S S S为空,则返回true,否则返回false。


总结:
在这里插入图片描述

3.1.2 栈的顺序存储实现

顺序栈的定义

#define MaxSize 10
typedef struct
{
    ElemType data[MaxSize];
    int top;	//栈顶指针(从0开始)
}SqStack;

初始化操作

void InitStack(SqStack &S)
{
    S.top = -1;
}
void testStack()
{
    SqStack S;
    InitStack(S);
    //...
}

判断栈空

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;
    S.data[S.top] = x;
    return true;
}
S.top = S.top + 1;
S.data[S.top] = x;

等价于

S.data[++S.top] = x;

出栈操作

bool Pop(SqStack &S, ElemType &x)
{
    if(S.top == -1)
        return false;
    x = S.data[S.top];
    S.top = S.top - 1;
    return true;
}
x = S.data[S.top];
S.top = S.top - 1;

等价于

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

读栈顶元素

bool GetTop(SqStack S, ElemType &x)
{
    if(S.top == -1)
    	return false;
    x = S.data[S.top];
    return true;
}

顺序栈的缺点:栈的大小不可变

共享栈:为了提高内存空间的利用率

#define MaxSize 10
typedef struct
{
    ElemType data[MaxSize];
    int top0;	//0号栈栈顶指针
    int top1;	//1号栈栈顶指针
}ShStack;
void InitStack(ShStack &S)
{
    S.top0 = -1;
    S.top1 = MaxSize;
}

总结:
在这里插入图片描述

3.1.2 栈的链式存储实现

链栈的定义

typedef struct LinkNode
{
    ElemType data;
    struct Linknode *next;
} LinkNode, *LinkStack;

在这里插入图片描述

(以下部分是自己写的,不保证正确)

链栈的初始化

//【不带头结点】
bool InitLinkStack(LinkStack &S)
{
    S = NULL;	//空表,暂时还没有任何结点
    return true;
}
//【带头结点】
bool InitLinkStack(LinkStack &S)
{
    S = (LinkNode *)malloc(sizeof(Linknode));
    if (S == NULL)
        return false;
    S->next = NULL;
    return true;
}

链栈的进栈

bool Push(Linknode &S, ElemType x)
{
    if(S == NULL)	//不带头结点
    {
        S = (LinkNode *)malloc(sizeof(LinkNode));
        S->next == NULL;
    }
    Linknode r = (LinkNode *)malloc(sizeof(LinkNode));
    if(r == NULL)	//内存不足
        return false;
    r->data = x;
    r->next = S->next;
    S->next = r;
    return true;
}

链栈的出栈

bool Pop(LinkStack &S, ElemType &x)
{
    if(S == NULL)
        return false;
    if(S->next == NULL)
        return false;
    LinkStack r = S;
    x = S->next->data;
    S->next = S->next->next;
    free(r);
    return true;    
}

获取栈顶元素

bool GetTop(LinkStack S, ElemType &x)
{
    if(S == NULL)
        return false;
    if(S->next == NULL)
        return false;
    x = S->next->data;
    return true;
}

判断栈空

bool StackEmpty(LinkStack S)
{
    return (S == NULL || S->next == NULL);
}

判断栈满

(链栈会满吗?黑人问号???)
在这里插入图片描述


总结:
在这里插入图片描述

3.2 队列

3.2.1 队列的基本概念

队列:队列是只允许在一端进行插入,在另一端删除线性表

先进先出, FIFO, First In First Out
在这里插入图片描述
队列的基本操作

InitQueue(&Q)初始化队列。构造一个空队列 Q Q Q分配内存空间

DestroyQueue(&Q)销毁队列。销毁队列,并释放队列 Q Q Q所占用的内存空间

EnQueue(&Q,x)入队。若队列 Q Q Q未满,将 x x x加入成为新队尾

DeQueue(&Q,&x)出队。若队列 Q Q Q非空,则删除队头元素,并用 x x x返回。

GetHead(Q,&x)读队头元素。若队列 Q Q Q非空,则用 x x x返回队头元素。

QueueEmpty(Q):判断一个队列 Q Q Q是否为空。若 Q Q Q为空,则返回true,否则返回false。


总结:
在这里插入图片描述

3.2.2 队列的顺序存储实现

队列的顺序实现

#define MaxSize 10
typedef struct
{
    ElemType data[MaxSize];
    int front,rear;	//队头指针和队尾指针。
} SqQueue;
  1. 从rear入队,从front出队
  2. rear指向队尾元素后一个位置

初始化队列

void InitQueue(SqQueue &Q)
{
    Q.rear = Q.front = 0;
}

判断队列是否为空

bool QueueEmpty(SqQueue Q)
{
    if(Q.rear = Q.front)
        return true;
    else
        return false;
}

入队操作(循环队列)

bool EnQueue(SqQueue &Q, ElemType x)
{
    if((Q.rear+1) % MaxSize == Q.front)	//判断队满
        return false;
    Q.data[Q.rear] = x;
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

这张图是队满的示意图。
在这里插入图片描述

这里牺牲一个存储单元,是因为如果全都存储了数据,那么rear和front就会重合,被认为是空队列。

队列元素个数:

(rear + MaxSize - front) % MaxSize

如果不想浪费这个存储单元,可以考虑如下方案:

#define MaxSize 10
typedef struct
{
 ElemType data[MaxSize];
 int front,rear;	//队头指针和队尾指针。从rear入队,从front出队。
 int size;		//队列当前长度
} SqQueue;

当插入成功时,size++;当删除成功时,size–。因此可以根据size的大小来判断是否队满。
在这里插入图片描述

出队操作(循环队列)

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

总结:
在这里插入图片描述

3.2.3 队列的链式存储实现
typedef struct LinkNode
{
    ElemType data;
    struct LinkNode *next;
}LinkNode;
typedef struct
{
    LinkNode *front, *rear;
}LinkQueue;

初始化队列

//【带头结点】
void InitQueue(LinkQueue &Q)
{
    //初始时front、rear都指向头结点
    Q.front = Q.rear = (LinkNode *)malloc(sizeof(LinkNode));
    Q.front->next = NULL;
}
//【不带头结点】
void InitQueue(LinkQueue &Q)
{
    Q.front = NULL;
    Q.rear = NULL;
}

判断队列是否为空

//【带头结点】
bool IsEmpty(LinkQueue Q)
{
    if(Q.front == Q.rear)
    	return true;
    else
        return false;
}
//【不带头结点】
bool IsEmpty(LinkQueue Q)
{
    if(Q.front == NULL)	//【or】if(Q.rear == NULL)
    	return true;
    else
        return false;
}

入队

//【带头结点】
void EnQueue(LinkQueue &Q, ElemType x)
{
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
    s->data = x;
    s->next = NULL;
    Q.rear->next = s;	//新结点插入到rear之后
    Q.rear = s;			//修改表尾指针
}

在这里插入图片描述

//【不带头结点】
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;			//修改表尾指针
    }

}

在这里插入图片描述

出队

//【带头结点】
bool DeQueue(LinkQueue &Q, ElemType &x)
{
    if(Q.front == Q.rear)
        return false;
    LinkNode *p = Q.front->next;
    x = p->data;
    Q.front->next = p->next;
    if(Q.rear == p)
        Q.rear = Q.front;
    free(p);
    return true;
}

在这里插入图片描述

//【不带头结点】
bool DeQueue(LinkQueue &Q, ElemType &x)
{
    if(Q.front == Q.rear)
        return false;
    LinkNode *p = Q.front;
    x = p->data;
    Q.front->next = p->next;
    if(Q.rear == p)
        Q.front = NULL;
    	Q.rear = NULL;
    free(p);
    return true;
}

队列满的条件

一般不会满,除非内存不足。


总结:
在这里插入图片描述

3.2.4 双端队列

双端队列:双端队列是只允许从两端插入、两端删除线性表

输入受限的双端队列:只允许从一端插入、两端删除线性表

输出受限的双端队列:只允许从两端插入、一端删除线性表


总结:
在这里插入图片描述

3.3 栈和队列的应用

3.3.0 C++中stack类的用法

stack常用操作

stack<int> q;	//以int型为例
int x;
q.push(x);		//将x压入栈顶
q.top();		//返回栈顶的元素
q.pop();		//删除栈顶的元素
q.size();		//返回栈中元素的个数
q.empty();		//检查栈是否为空,若为空返回true,否则返回false

例子:

#include<iostream>
#include<stack>
using namespace std;
int main()
{
	stack<int>  q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	q.push(5);
	
	cout<<"q.size "<<q.size()<<endl;
	cout<<"q.top "<<q.top()<<endl;   //输出栈顶元素 
	
	q.pop();	//删除栈顶元素
			
	cout<<"q.size "<<q.size()<<endl;  
	cout<<"q.top "<<q.top()<<endl;
	
	return 0; 
}
q.size 5
q.top 5
q.size 4
q.top 4
3.3.1 栈在括号匹配中的应用

在这里插入图片描述

3.3.2 栈在表达式求值中的应用
中缀表达式后缀表达式前缀表达式
a + ba b ++ a b
a + b - ca b + c -- + a b c
a + b - c * da b + c d * -- + a b * c d

将中缀表达式转为后缀表达式

“左优先”原则:只要左边的运算符能先计算,就优先算左边的。
在这里插入图片描述

机算步骤如下:

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

用栈实现后缀表达式的计算

  1. 从左往右扫描下一个元素,直到处理完所有元素。
  2. 若扫描到操作数则压入栈,并回到1;否则执行3。
  3. 若扫描到运算符,则弹出两个栈顶元素,执行相应计算,运算结果压回栈顶,回到1。

(前缀表达式类似,只是从右往左扫描)

用栈实现中缀表达式的计算

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

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

  • 递归调用时,函数调用栈可称为“递归工作栈”
  • 每进入一层递归,就将递归调用所需信息压入栈顶
  • 每退出一层递归,就从栈顶弹出相应信息
  • 缺点:太多层递归可能会导致栈溢出
3.3.4 队列的应用

树的层次遍历
在这里插入图片描述

图的广度优先遍历
在这里插入图片描述

操作系统
在这里插入图片描述

3.4 稀疏矩阵的压缩存储

法1:
在这里插入图片描述

法2:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Quentin_HIT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值