一、栈
1.1栈的基本概念
1.1.1栈的定义
栈的逻辑结构是线性结构,是一种受限线性表(受到特定的限制)
栈是只允许在一端进行插入或删除操作线性表,特点是先进后出(后进先出)(LIFO)。例如撤销的实质就是栈的应用。
1.1.2栈的结构
栈顶:允许进行插入删除那一端
栈底:不允许进行插入删除的一端
1.1.3出栈顺序的排列组合数问题
n个不同元素进栈,出栈元素不同排列个数为 :
1.2栈的顺序存储结构
1.2.1顺序栈实现
使用顺序存储的栈称为顺序栈,利用一组地址连续的存储单元存放数据元素
typedef struct{
Elemtype data[MaxSize];栈元素
int top; //栈顶指针
}SqStack;
1.2.2栈顶指针指向不同入栈出栈问题(图示)
栈顶指针:S.top
初始化为-1的情况下,入栈要使top先自加
S.data[++S.top] = x;
出栈时要是top后减
S.data[S.top–] = x
初始化为0的情况下,刚好与-1时相反
入栈要使top后加
S.data[S.top++] = x;
出栈时要是top先减
S.data[–S.top] = x
1.2.3栈空、栈满判断(top指向 -1情况)
栈空:S.top == -1;
栈满: S.top == MaxSize - 1;
1.2.4 顺序栈基本操作实现(C++代码)
1.2.4.1初始化
void InitStack(SqStack &s){
S.top == -1;
}
1.2.4.2判断栈空
bool StackEmpty(SqStack S){
if(S.top == —1)
return true;
else
return false;
}
1.2.4.3进栈
bool Push(SqStack &S,ElemType X){
if(S.top == MaxSize - 1)// 判断栈是否已经满
return false;
S.data[++S.top] = x ;
return ture;
1.2.4.4出栈
bool Pop(SqStack &S,ElemType X){
if(S.top == -1)// 判断栈是否空
return false;
x = S.data[S.top--];
return true;
1.2.4.5读栈顶元素
bool GetTop(SqStack &S,ElemType X){
if(S.top == -1)// 判断栈是否空
return false;
x = S.data[S.top];
return true;
1.2.5共享栈问题
将两个栈的栈底分别设置在共享空间的两端.
作用:更有效的利用存储空间,减少上溢的可能
判断左端栈空 top0 = -1,判断右端栈空top1 = MaxSize
判断栈满情况top1 - top0 = 1 (两个栈相邻)
1.3栈的链式存储结构
采用链式存储的栈称为链栈
存储类型课描述为
typedef struct Linknode{
ElemType data;
struct Linknode *next;
} *LiStack;
便于多个栈共享存储空间和提高效率,不存在栈满上溢的情况
1.4 栈错题
一个栈的入栈序列为1,2,3…n,出栈序列是P1,P2,…PN。若p2=3,则P3可能取值的个数是(C)
A. n - 3 B.n - 2 C.n - 1 D.无法确定
分析:4 - n 均为 p3可取的值(例如:4入栈出栈取4,4.5入栈5出栈取5)
讨论1.2,当P1 = 2 时 P3可以取1(1入栈,2入栈出栈,3入栈出栈)
P1 = 1时 P3可以取2(1入栈出栈,2、3入栈,3出栈,2出栈)
最终总数为 N - 1
二、队列
2.1队列的基本概念
2.1.1队列的定义
操作受限的线性表(只允许在表的一端进行插入,在表的另一端进行删除。
其特点是先进先出(FIFO)
队头:允许删除的一端
队尾:允许插入的一端
2.2队列的顺序存储结构
2.2.1结构描述
typedef struct{
ElemType data[MaxSize];
int front,int rear;//队头指针队尾指针
} SqQueue;
可以利用Q.front == Q.rear == 0 来判断队列是否为空
但不能用Q.rear == MaxSize 作为队列满的条件
2.2.2基本操作
2.2.2.1初始化队列
void InitQueue(SqQueue &Q)
{
Q.rear = Q.front = 0;
}
2.2.2.2判断队列是否为空
bool QueueEmpty(SqQueue Q)
{
if(Q.rear == Q.front)
return true;
else
return false;
2.2.2.3入队
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;
2.2.2.4出队
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;
2.2.3循环队列的问题
循环队列(逻辑上变成一个循环的队列,但实际上还是顺序存取)里的取余:把无限个正数映射到有限个数字。
其中的 进一操作
队首指针进一 Q.front = (Q.front + 1 ) % MaxSize;
队尾指针进一 Q.rear = (Q.rear + 1 ) % MaxSize;
队列长度 (Q.rear + MaxSize - Q.front)%MaxSize;
2.2.3队列是否满的问题
- 牺牲一个单元“队头指针在队尾指针的下一个位置作为队满的标志”
(Q.rear + 1 ) % MaxSize == Q.front - 设置一个计数器来计数,当Q.size == 0 队空,Q.size == MaxSize 队满
- 设置标记位,tag = 0,如果是因为删除导致 Q.front == Q.rear,那么就判断为队空 。tag = 1,如果应为插入导致则为队满 。
2.3队列的链式存储结构
同时带有头指针和尾指针的单链表
typedef struct {
ElemType data;
struct LinkNode * next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
2.4链式队列的基本操作
2.4.1初始化
void InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
2.4.2判断队空
bool IsEmpty(LinkQueue Q)
{
if(Q.front == Q.rear)
return true;
else
return false;
}
2.4.3入队
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;s->next = NULL;
Q.rear->next = s;
Q.rear = s;
2.4.4 出队
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.rear == Q.front)
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);
reutrn true;
2.4 双端队列
允许两端都可以就入队与出队的操作队列
输出受限双端队列:允许一端进行插入和删除,但另一端只能进行插入
输入受限双端队列:允许一端进行插入和删除,但另一端只能进行删除
总结
栈总特点:先进后出
顺序栈,共享栈,链栈他们的特点。
栈的基本操作
队列总特点“先进先出"
指针进一规则,判断队满的三种情况