栈
栈(stack)是限定仅在表尾(栈顶)进行插入和删除操作的线性表。
我们将允许插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。
栈又称后进先出(Last In First Out)的线性表,也就是说,栈具有线性关系,即前驱后继关系。又简称LIFO结构。
栈的插入操作(push):进栈,也称压栈,入栈。
栈的删除操作(pop):出栈,也称弹栈。
插入删除操作如图所示:
合法出栈数:
(5条消息) 栈和卡特兰数(Catalan number)_LolitaAnn的博客-CSDN博客_卡特兰数求出栈公式
凡是合法序列都遵循以下规律:即对于出栈序列中的每一个数字,在它后面的、比它小的所有数字,一定是按递减顺序排列的。
详见上面这篇博客。
栈的顺序存储结构:
当栈存在一个元素时,top=0,因此常把空栈的判定条件定位top=-1;
同理,top=n时,栈满。
示例·pop:
int Pop(Stack *S,int *e){
if(S->top==-1){ return ERROR; }
*e=S->data[S->top];
S->top--;
return OK;
}
push同理可得;
两栈共享空间
如图4-5-1:
pop操作:
常在两栈空间需求关系相反时使用。
栈的链式存储结构:
链栈:将单链表头指针与栈顶指针合二为一,则此时头结点失去意义,通常对链栈来说,不需要头结点。
空栈判定:top=NULL;
代码示例及详解:
#include<stdio.h>
#include<stdlib.h>
typedef struct StackNode {
int data;
struct StackNode* next;
}*LinkStackPtr/*ptr:pointer*/;
typedef struct LinkStack {
LinkStackPtr top;/*top链栈结构体指针,StackNode*型*/
/*和链表头指针无太大区别,和原先栈顶指针产生差异*/
/*原先top只负责数组中的索引功能,记录值还是由数组完成的*/
/*现在top是一个真正的指针,它指向栈顶元素的地址*/
/*所以S->top->data是栈顶元素*/
int cnt;//相当于原本的top,负责记录栈顶元素的位置
} LinkStack;
int Push(LinkStack *S, int e) {
LinkStackPtr p = (LinkStackPtr)malloc(sizeof(struct StackNode)); //申请内存
p->data = e;
p->next = S->top;
S->top = p;
S->cnt++;//将栈顶拉高1
return 1;//OK
}
//S->top:a[n-1] S->top->next:a[n-2]
int Pop(LinkStack *S, int *e) {
if (S->cnt == -1) {
return 0;
}
LinkStackPtr p;
*e = S->top->data;
p = S->top;//one
S->top = S->top->next;
S->cnt--;
free(p);//two The steps one and two are important.
return 1;
}
栈的应用:四则运算问题(带括号)
思路:1.利用栈进出运算符来将中缀表达式转换成后缀表达式
2.利用栈进出运算数字将后缀表达式处理出结果
队列:
(queue)只允许在一端执行插入,而在另一端执行删除操作的线性表。
先进先出(FIFO),允许插入的一端称为队尾,允许删除的一端称为队头。
队列的顺序存储结构:
显然,队列的顺序存储结构较难满足我们对队列的要求(可以通过循环队列来进行优化),于是,我们考虑链式存储结构。
队列的链式存储结构(链队列):
参考博客:
代码示例:
#include<stdio.h>
#include<stdlib.h>
typedef struct QNode {
int data;
struct QNode* next;
}*Qptr;
typedef struct {
Qptr head, rear;
} LinkQueue;
int EnQueue(LinkQueue *Q, int e) {
Qptr p = (Qptr)malloc(sizeof(struct QNode));
if (!p)
exit(EOVERFLOW);
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
return 1;
}
int DeQueue(LinkQueue *Q, int* e) {
Qptr p;
//拥有头结点,故是->next->next
if (Q->head == Q->rear)
return 0;
p = Q->head->next;
*e = p->data;
Q->head->next = p->next;
if (Q->rear == p)
Q->rear = Q->head;
free(p);
return 1;
}
小结:
其实二者本质上是一种特殊一些的单链表而已。
链栈不需要带头结点,而链队列带头结点更为便捷。
下篇文章讲串~~