2.2 堆栈
堆栈和队列都是对线性表做一些特殊的限制,形成了两种存储数据的结构,一个是先进后出、一个是先进先出、可以解决实际生活中遇到的一些问题,数据库也可以理解成一种数据结构,只不过这种结构比较普通罢了。
2.2.1 什么是堆栈
先进后出,后进先出的线性表就是堆栈,相当于对线性表的操作做一些特殊的限制
- 计算机计算表达式求值:后缀表达式(运算符号在运算数的后面,从左往右扫描,逐个的处理运算数和运算符号)。遇到运算数存下来,遇到符号就取出之前存的两个数字做运算,完成之后再存回去,最后弹出结果。
堆栈: 有一定操作限制的线性表,只在一端做插入、删除。
数据对象集: 一个有0个或多个元素的有穷线性表
操作集:
Stack CreateStack(int MaxSize); //生成空堆栈,长度为MaxSize();
int IsFull(Stack s, int MaxSize); //判断堆栈s是否已满。
void Push(Stack s, ElementType item); //把元素item压入堆栈.
int IsEmpty(Stack s); //判断堆栈是否为空。
ElementType Pop(Stack s); //删除栈顶元素,并返回新的栈顶元素
2.2.2 堆栈的顺序存储
用数组存储,最后一个元素的下标即为top,空栈时top为-1。
#define MaxSize <存储数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
ElementType Data[MaxSize];
int top;
};
- 压栈
void Push(Stack s, ElementType x){
if(s->top == MaxSize-1){
printf("堆栈满!");
}else{
top++;
s->Data[s->top] = x;
}
return;
}
- 出栈
ElementType Pop(Stack s){
ElementType x;
if(s->top == -1){
printf("栈空!");
return ERROR;
}
else{
//如果不设置局部变量,可用:return s->Data[(s->top)--];
x = s->Data[s->top];
s->top--;
return x;
}
}
用一个数组存储两个堆栈,让堆栈从两边向中间生长。
typedef struct DSNode *DStack
struct DSNode{
ElementType Data[MaxSize];
int top1 = -1;
int top2 = MaxSize;
};
- 入栈
//Tag用来区分这两个堆栈,0表示第一个,1表示第二个。
void Push(DStack s, ElementType x, Tag i){
if((s->top2 - s->top1) == 1){
printf("栈满!");
return ERROR;
}else{
if(!Tag)
s->Data[++(s->top1)] = x;
else
s->Data[--(s->top2)] = x;
return;
}
}
- 出栈
ElementType Pop(DStack s, int Tag){
if(!Tag){
if(s->top1 == -1){
printf("空栈!");
return ERROR;
}else return s->Data[(s->top1)--];
}else{
if(s->top2 == MaxSize){
printf("空栈!");
return ERROR;
}else return s->Data[(s->top2)++];
}
}
2.2.3 堆栈的链式存储
用单向链表实现堆栈,只能在链头操作(一个链表节点不知道它的上一个节点在哪里)
typedef struct SNode *Stack;
struct SNode{
ElementType Data;
Stack NEXT;
};
- 创建一个空栈
//这个头结点不代表任何元素,一直存在在堆栈头,只是为了方便堆栈的插入删除操作。
Stack CreateStack(){
Stack s = (Stack)malloc(sizeof(struct SNode));
S->NEXT = NULL;
return s;
}
//可以通过判断头结点的NEXT是否为NULL来得知是否为空。
int IsEmpty(Stack s){//为空返回1,不空返回0
if(s->NEXT == NULL)
return 1;
else
return 0;
}
- 入栈
void Push(ElementType x, Stack s){
Stack p = (Stack)malloc(sizeof(struct SNode));
p->Data = x;
p->NEXT = s->NEXT;
s->NEXT = p;
}
- 出栈
Stack Pop(Stack s){
if(IsEmpty(s)){
printf("空栈!");
return NULL;
}else{
Stack p = s->NEXT;
s->NEXT = p->NEXT;
free(p);
return s->NEXT;
}
}
2.2.4 堆栈应用:表达式求值
中缀表达式转换为后缀表达式:
运算数:直接输出
左括号:压栈
右括号:出栈,直到遇到左括号
运算符:遇到运算符号先入栈,直到遇到比栈顶符号优先级低的,出栈,然后该符号再和下一个栈顶元素比较,直到比栈顶高级或空栈。
后缀表达式求值:
运算数:入栈
运算符:弹出合适数量的运算数做计算
2.3 队列
插入(入队AddQ)和删除(出队DeleteQ)位于队列两头。
类型名称: 队列(Queue)
数据对象集: 一个有0个或多个元素的有穷线性表。
操作集:
Queue CreateQueue(int MaxSize);
int IsFull(Queue Q, int MaxSize);
void AddQ(Queue Q, ElementType x);
int IsEmpty(Queue Q);
ElementTpye DeleteQ(Queue Q);
队列的顺序存储实现
#define MaxSize<队列的最大个数>
typedef struct QNode *Queue;
struct QNode{
ElementType Data[MaxSize];
front;//队列头
rear;//队列尾
};
- 入队
void AddQ(Queue Q, ElementType x){
if((Q->rear+1)%MaxSize == Q->front){
printf("队列满");
return;
}
Q->rear = (Q->rear+1)%MaxSize;
Q->Data[Q->rear] = x;
}
- 出队
ElementType DeleteQ(Queue Q){
if(Q->rear == Q->front){
printf("队列空");
return NULL;
}
Q->front = (Q->front+1)%MaxSize;
return Q->Data[Q->front];
}
队列的链式存储
front设在链表头,rear设在链表尾
typedef struct Node *List
struct Node{
ElementType Data;
List NEXT;
};
struct QNode{
List front;
List rear;
};
typedef struct QNode *Queue//一个QNode类型的指针,不同的指针表示不同的队列。
- 入队
void AddQ(Queue Q, ElementType x){
List s = (List)malloc(sizeof(struct Node));
s->Data = x;
s->NEXT = NULL;
if(Q->rear == NULL){
Q->rear = s;
return;
}
Q->rear->NEXT = s;
Q->rear = s;
return;
}
- 出队
ElementType DeleteQ(Queue Q){
if(Q->front == NULL){
printf("栈空!");
return NULL;
}
//如果只有一个元素,就把rear和front都置为NULL
if(Q->rear == Q->front)Q->rear = NULL;
List s = Q->front;
ElementType x = s->Data;
//如果只有一个元素那么s的NEXT就是NULL,这里使用小技巧同时涵盖了一个或多个元素的情况
Q->front = s->NEXT;
free(s);
return x;
}