定义: 栈是一种只能在一端进行插入或删除操作的线性表. 所谓栈是线性表, 是指栈的逻辑结构属于线性表, 只不过在操作上加了一些约束. 栈的可以插入或删除元素的一端叫栈顶, 另一端叫栈底. 栈是一种先进后出的结构. First In, Last Out (FILO).
图1. 元素入栈和出栈
1. 顺序栈
顺序栈即把栈用数组的形式实现. 我们用int stack[MAX_SIZE] 存储栈中的元素, 用一个int类型的数据top记录栈顶元素的数组下标, 我们称top为栈顶指针( 这个指针只是记录了数组下标, 并不是真正的指针), 当stack中没有数据时, top值为-1. 注意: top的值在不同的场合会有不同的规定, 但都要满足先进后出原则.
栈的最重要的操作为入栈和出栈.
图2. 顺序表元素入栈
图3. 顺序表元素出栈
除了栈空的情况, top一直指向当前的栈顶元素, 出栈后的元素仍在数组里, 但已不属于栈里的元素, 重新执行入栈操作会将这些出栈的元素覆盖掉.
入栈: 先移动栈顶指针top, 再入栈元素;
出栈: 先取出栈顶元素, 再移动栈顶指针.
注意: 以上的入栈和出栈的操作方式只是可以实现栈的先进后出的特性的其中一种配置方法. 我们当然也可以采取其它的顺序同样实现栈的特性, 这个一定要灵活, 可能在不同的题目中看到不同的配置方法. 比如先入栈元素, 再移动栈顶指针, 有时需要根据题目条件来判断具体属于哪一种配置方法.
图4. 顺序表判断栈空和栈满
#include <iostream>
const int MAX_SIZE = 10;
/// <summary>
/// 顺序栈的结构体
/// </summary>
typedef struct {
int data[MAX_SIZE];
int top;
}SqStack;
/// <summary>
/// 初始化一个栈
/// </summary>
/// <param name="S"></param>
void initStack(SqStack &S) {
S.top = -1;
}
/// <summary>
/// 元素入栈
/// </summary>
/// <param name="S"></param>
/// <param name="element">入栈元素</param>
void push(SqStack& S, int element) {
S.top++;
S.data[S.top] = element;
}
/// <summary>
/// 元素出栈
/// </summary>
/// <param name="S"></param>
/// <returns></returns>
int pop(SqStack& S) {
int element = S.data[S.top];
S.top--;
return element;
}
/// <summary>
/// 判断栈是否为空, 为空返回true, 不为空返回false
/// </summary>
/// <param name="S"></param>
/// <returns></returns>
bool stackEmpty(SqStack& S) {
if (S.top == -1) {
return true;
}
return false;
}
/// <summary>
/// 判断栈是否为满, 为满返回true, 不为满返回false
/// </summary>
/// <param name="S"></param>
/// <returns></returns>
bool stackFull(SqStack& S) {
if (S.top == MAX_SIZE - 1) {
return true;
}
return false;
}
int main()
{
SqStack S;
initStack(S);
int i = 0;
while (!stackFull(S)) {
printf("入栈元素: %d\n", i);
push(S, i);
i++;
}
printf("-----------------------------\n");
while (!stackEmpty(S)) {
printf("出栈元素: %d\n", pop(S));
}
}
代码1: 顺序栈的基本操作
2. 链式栈
一般使用一个带头结点的单链表来实现链式栈.
图5. 链式栈入栈
如图, 入栈元素插入到头结点后面, 头结点的后面的元素为栈顶, 链表的最后一个元素为栈底.
图6. 链式栈出栈
如图, 将头结点的后面的元素出栈.
图7. 链式栈判断栈空
只要有足够的内存, 栈就不会满. 考研中默认链式栈不会满, 即没有栈满状态.
#include <iostream>
/// <summary>
/// 单链表结点定义
/// </summary>
typedef struct LNode {
int data;
struct LNode* next;
}LNode;
/// <summary>
/// 初始化一个栈
/// </summary>
/// <param name="H"></param>
void initStack(LNode*& H) {
H = (LNode*)malloc(sizeof(LNode));
H->data = NULL;
H->next = NULL;
}
/// <summary>
/// 元素入栈
/// </summary>
/// <param name="H">链表头结点</param>
/// <param name="element">入栈元素</param>
void push(LNode* H, LNode* element) {
element->next = H->next;
H->next = element;
}
/// <summary>
/// 元素出栈
/// </summary>
/// <param name="H">链表头结点</param>
/// <returns></returns>
LNode pop(LNode* H) {
LNode* element;
element = H->next;
H->next = H->next->next;
return *element;
}
/// <summary>
/// 判断栈是否为空, 为空返回true, 不为空返回false
/// </summary>
/// <param name="H">链表头结点</param>
/// <returns></returns>
bool stackEmpty(LNode* H) {
if (H->next == NULL) {
return true;
}
return false;
}
int main()
{
LNode* H;
initStack(H);
int i = 0;
while (i < 10) {
printf("入栈元素: %d\n", i);
LNode* element = (LNode*)malloc(sizeof(LNode));
element->data = i;
element->next = NULL;
push(H, element);
i++;
}
printf("-----------------------------\n");
while (!stackEmpty(H)) {
printf("出栈元素: %d\n", pop(H).data);
}
}
代码2: 链式栈的基本操作