堆栈(Stack),具有一定操作约束的线性表。(后入先出,LIFO)
只在一端(栈顶,Top)做插入、删除。
插入 push,删除pop
一、栈的顺序存储及操作实现
通常由一个一维数组+一个记录栈顶元素位置的变量组成
(1)定义
//定义
#define MaxSize <存储数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
ElementType Data[MaxSize];
int Top; //记录栈顶元素的位置
};
(2)入栈
//入栈
void Push(Stack Ptrs,ElementType item)
{
if(Ptrs->Top == MaxSize-1)
{
printf("堆栈满");
return ;
}else{
//(Ptrs->Top)++;
//Ptrs->Data[Ptrs->Top]++ = item;
Ptrs->Data[++(Ptrs->Top)] = item;
}
}
(3)出栈
//出栈
ElementType Pop(Stack Ptrs)
{
if(Ptrs->Top == -1)
{
printf("堆栈空");
return ERROR; //ERROR是ElementType的特殊值,标记错误
}else
{
return(Ptrs->Data[(Ptrs->Top)--]);
}
}
(4)用一个数组实现两个堆栈(最大利用数组空间)
使这两个栈分别从数组的两头向中间生长;当两个栈顶指针相遇时,表示两个栈都满了。
// 用一个数组实现两个堆栈
#define MaxSize<存储数据元素的最大个数>
struct DStack
{
ElementType Data[MaxSize];
int Top1;
int Top2;
}S;
S.Top1 = -1;
S.Top2 = MaxSize;
void Push(struct DStack *Ptrs, ElementType item, int tag)
{
if(Ptrs->Top2 - Ptrs->Top1 == 1) //两个栈顶指针相遇,堆栈满
{
printf("堆栈满");
return ;
}
if(tag == 1)
Ptrs->Data[++(Ptrs->Top1)] = item;
else
Ptrs->Data[--(Ptrl->Top2)] = item;
}
ElementType Pop( struct DStack *Ptrs, int tag)
{
if(tag == 1)
{
if(Ptrs->Top1 == -1)
{
printf("栈堆1空");
return NULL;
}else
{
return Ptrs->Data[(Ptrs->Top1)--];
}
}
else
{
if(Ptrs->Top2 == MaxSize) //初始值S.Top2 = MaxSize;
{
printf("堆栈2空");
retrun ;
}else
{
return Ptrl->Data[(Ptrs->Top2)++];
}
}
}
二、堆栈的链式存储及操作实现
实际上是一个单链表(链栈)。
链表头作为Top。
(1)建立链栈
①堆栈初始化(建立空栈)
②判断堆栈S是否为空
//建立
typedef struct SNode *Stack;
struct SNode
{
ElementType Data;
struct SNode *Next;
} ;
Stack CreateStack()
{
//构建一个堆栈的头结点,返回指针
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
int IsEmpty(Stack S)
{
//堆栈S为空则返回1,不为空则返回0
return (S->Next == NULL);
}
(2)push操作
//push操作
void Push(ElementType item, Stack S)
{
struct SNode *TmpCell;
TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
TmpCell->Element = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
(3)pop操作
数组空间有限,但是链表无限,所以链表无需判断堆栈是否已满,只需要判断是否为空。
//pop操作
ElementType Pop(Stack S)
{
//删除并返回堆栈S的栈顶元素
struct SNode *FirstCell;
ElementType TopElement;
if(IsEmpty( S ))
{
printf("堆栈空");
return NULL;
}
else
{
FirstCell = S->Next;
S->Next = FirstCell->Next;//第一个链上第三个
TopElement = FirstCell-> Element;
free(FirstCell);
return TopElement;
}
}
三、堆栈应用
1.表达式求值
(1)后缀表达式求值
从左到右读入后缀表达式各项。
①运算数:入栈;
②运算符:从堆栈弹出适当数量的运算数,计算并结果入栈;
③最后,堆栈顶的元素就是表达式的结果值。
(2)中缀表达式求值
基本策略:中缀表达式——>后缀表达式
例子:2+9/3-5 ——>2 9 3 / + 5-
- 运算数相对顺序不变
- 运算符号顺序发生改变
- 需要存储“等待中”的运算符号
- 要将当前运算符号与“等待中”的最后一个运算符号比较
- T(N) = O(n) 时间复杂度线性
将中缀转换为后缀:从到到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理
①运算数:直接输出;
②左括号:压入堆栈
③右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
④运算符:若优先级大于栈顶运算符,则把它压栈;
若优先级小于等于栈顶运算符,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符优先级大于栈顶运算符为止,然后将该运算符压栈;
⑤若各对象处理完毕,则把堆栈中残留的运算符一并输出。
2.堆栈的其他应用
(1)函数调用及递归实现(倒序输出)
(2)深度优先搜索(借助递归,实际也是堆栈)
(3)回溯算法(老鼠走迷宫、试探可能性、返回)
... ...