栈(Stack):只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在一端进行插入和删除操作。如图:
栈顶(Top):线性表允许进行插入和删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。
特点:后进先出。
栈的顺序存储也称为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个栈顶(Top)指针。
栈的顺序存储类型可描述为:
#define MaxSize 50 //定义栈中元素的最大个数 typedef struct{ ElemType data[MaxSize]; //存放栈中元素 int top; //栈顶指针 }SqStack;栈顶指针:S.top,初始时设置 S.top = -1;栈顶元素:S.data[S.top]。
进栈操纵:栈不满时,栈顶指针先加 1,再送值到栈顶元素。
出栈操作:栈非空时,先去栈顶元素,在将栈顶指针减 1。
栈空条件:S.top == -1;栈满条件:S.top == MaxSize - 1;栈长:S.top + 1。
栈顶指针与栈中元素之间的关系:
初始化
void InitStack(&S){ S.top = -1; //初始化栈顶指针 }判栈空
bool StackEmpty(S){ if(S.top == -1) return true; else return false; }进栈
bool Push(SqStack &S,ElemType x){ if(S.top == MaxSize-1) //栈满,报错 return false; S.data[++S.top] = x; //指针先加 1,再入栈 return true; }出栈
bool Pop(SqStack &S,ElemType &x){ if(S.top == -1) //栈满,报错 return false; x = S.data[S.top--]; //先出栈,指针再减 1 return true; }读栈顶元素
bool GetTop(SqStack S,ElemType &x){ if(S.top == -1) return false; x = S.data[S.top]; return true; }利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸,如图:
这里规定链栈没有头结点,LHead 指向栈顶元素,如图:
栈的链式存储类型描述为:
typedef struct LinkNode{ ElemType data; //数据域 struct LinkNode *next; //指针域 }*LiStack;队列(Queue):队列简称队,也是一种操作受限的线性表,只允许在表的一端插入,而在表的另一端进行删除。先进先出。
队头(Front):允许删除的一端,又称为队首。
队尾(Rear):允许插入的一端。
空队列:不包含任何元素的空表。
设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置。
#define MaxSize 50 typedef struct{ ElemType data[MaxSize]; int front,rear; //队头队尾指针 }SqQueue;初始状态(队空条件):Q.front == Q.rear == 0;
进队操作:队不满时,先送值到队尾元素,再将队尾指针加 1。
出队操作:队不空时,先取队头元素值,再将队头指针加 1。
初始时:Q.rear = Q.front = 0;
队首指针进 1:Q.front = (Q.front + 1)%MaxSize;
队尾指针进 1 :Q.rear = (Q.rear + 1)%MaxSize;
队列长度:(Q.rear - Q.front + MaxSize)%MaxSize。
但是还是会出现队空和队满条件一样的情况,即条件都是 Q.rear == Q.front。为了区分队空还是队满,可以采取以下三种方法:
牺牲一个单元来区分队空和队满;类型中增加表示元素个数的数据成员;类型中增加 tag 数据成员,bool 值表示队空和队满。初始化
void InitQueue(&Q){ Q.front = Q.rear = 0; //初始化队首队尾指针 }判空
bool isEmpty(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 ture; }出队
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 ture; }双端队列是指允许两端都可以进行入队和出队操作的队列,如图:
算法的基本思想:
初始化一个空栈,顺序读入括号;若是右括号,则或者置于栈顶的最急迫期待得以消除,或者是不合法的情况,或者是不合法的情况(括号序列不匹配,退出程序);若是左括号,则作为一个新的更急迫的期待压入栈中,自然使原有的在栈中的所有未消解的期待的急迫性将一级。算法结束时,栈为空,否则括号序列不匹配。将中缀表达式转换成后缀表达式的算法思想如下:
从左向右开始扫描中缀表达式;
遇到数字时,加入后缀表达式;
遇到运算时:
若为 “(” ,入栈;若为 “)” ,则依次把栈中的运算符加入后缀表达式中,直到出现 “)”,从栈中删除 “(”;若为除括号外的其他运算符,当其优先级高于除 “(” 以外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级相等的运算符,直到一个比它优先级低或者遇到一个左括号为止。当题目要求直接变换成后缀或前缀表达式时:
第一步:按照运算符的优先级对所有的运算单位加括号。
第二步:把运算符号移动到对应的括号前面;然后把括号去掉,就变成了前缀表达式;
第三步:把运算符号移动到对应的括号后面,然后把括号去掉,就变成了后缀表达式。