栈:仅在表头定义操作的线性表
引例:
运算数+运算符(前缀、中缀:a+b*c-d/e、后缀:abc*+de)
例1:(1)计算 62/3-42*+ =___
解: 6/2 = 3 --> 33- ---> 3-3 = 0 ---> 042* ---> 4*2 = 8 ---> 08+ ---> 8
故结果为 8
(2)计算
计算机从左向右扫描,遇到数字保存,遇到运算符取出最近保留的2个数字,需要时倒序输出 ---> 堆栈,先进后出,在一端做插入、删除
数据类型:Stack
数据对象集: 一个有0个或多个元素的有穷线性表
操作集: 长度为MaxSize的堆栈S∈Stack,堆栈元素 item ∈ ElementType
基本算法:
1、生成空堆栈,最大长度为 MaxSize: Stack CreateStack( int MaxSize )
2、判断S是否为满 : int IsFull(Stack S, int MaxSize)
3、将元素item压栈(插入/ 入栈) : void Push(Stack S, ElementType item)
4、判断是S否为空 : int IsEmpty( Stack S)
5、删除并返回栈顶元素 : ElementType Pop( Stack S )
例2: Push(S, A) Pop(A) Push(S, B) Push(S, C) Pop(C) Pop(B) 的结果为____
A C B
动脑:ABCD 有哪几种情况?哪些可行哪些不可行?
一、栈的顺序存储实现
1、基本算法:
1、 定义结构体 Stack
#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;
return;}
}
3、 出栈
ElementType Pop( Stack PtrS)
{
if( PtrS->Top == -1 )
{ printf("堆栈空");
return ERROR; } // ERROR
else
return( PTrS->Data[(PtrS->Top0--]);
}
2、利用一个数组实现两个堆栈,要求最大利用数组空间,是数组只要有空间入栈,操作就可以成功
不能两头从中间往外:因为会有空余的格子
方法:在数组的两头定义Top1 Top2, 分别存放栈顶元素,然后让元素依次从两头入栈,判断堆栈满:Top1、Top2相遇,而不是 Top1+Top2 = MaxSize
1、定义结构体
#define MaxSizez<允许存储的元素个数>
struct DStack
{
ElementType Data[MaxSize];
int Top1;
int Top2;
} S;
S.Top1 = -1; //判断堆栈空
S.Top2 = MaxSize; //
2、入栈
void Push( struct DStack *PtrS, ElementType item, int Tag)
{ //Tag取值1和2,判断是左边还是右边入栈
if( PtrS->Top2 - PtrS->Top1 == 1)
{ printf("堆栈满");
return;
}
if( Tag==1 )
{ PtrS->Data[++(PtrS->Top)] = item;
// Top1 原值为 -1,这里需要从下标[0]开始
}
else
{ PtrS->Data[--(PtrS->top)] = item;
//Top2 原值为 MaxSize, 这里需要先向前移一位再进行运算
}
}
3、 出栈
ElementType Pop( struct DStack *PtrS, int Tag) // Tag指示那边的栈
{
if( Tag ==1 )
{ if( PtrS->Top == -1)
{printf("堆栈1 空"); return NULL;}
else
return PtrS->Data[(PtrS->Top1)--]; // 运算完毕后,元素向前移一格(区别前缀)
}
else
{ if( PtrS->Top2 == MaxSize )
{printf(" 堆栈2 空"); return NULL;}
else
return PrtS->Data[(PtrS->Top2)++]; // 运算完毕后,元素向后移一格
}
}
二、堆栈的链式存储实现
单链表—链栈,插入、删除只在链栈的栈顶进行,(栈顶指针Top 应在链表哪一头?)
1、 定义结构体
typedef struct SNode *Stack;
struct SNode
{
ElementType Data;
struct SNode *Next;
};
2、堆栈初始化
Stack CreatStack() // 建立一个头结点S,指向后面的数据
{
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
3、 判断是否为空
int IsEmpty(Stack S)
{
return (S->Next == NULL); // 判断语句。若为空,则返回0; 否则,返回1
}
4、 插入
void Push(ElementType item, Stack S)
{
struct SNode *TmpCell;
TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
TmpCell->Data = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
5、 删除
ElementType Pop( Stack S )
{
struct SNode *FirstCell;
ElementType TopElem;
if( IsEmpty(S) ) // 判断链表是否为空
{ printf("堆栈空"); return NULL;}
else
{
FirstCell = S->Next;
S->Next = FirstCell->Next;
TopElement = FirastCell->Data; // 用于装要删除的元素值
free(FirstCell); // 清除此节点
return TopElement; // 返回要删除的元素的值
}
}
三、后缀、中缀转换
方式:
例3: (1)请详细写出表达式中缀a*(b+c)/d 转换为 后缀abc+*d/ 的过程 时间复杂度呈线性 T(N) = O(N)
(2)2*(6/3+4)-5 转化为后缀表达式时,堆栈元素最多为____(3)
(3) 中 -> 后: 2 *(9+6/3-5)+4 =________
解:(3)请看大图
堆栈其他应用: 1、函数调用即递归实现 2、深度优先搜索 3、回溯算法