前言:
在经历上一篇文章的“漫长”之后,我决定将必要的总计性文字置于前文,后文再扯的更详细。
总结:堆栈
堆栈(Stack):具有一定操作约束的线性表,即只在一端(栈顶,Top)做插入、删除,从而使之呈现后入先出的效果。实现方式依旧是数组(顺序储蓄)和链表。图示:
例:进入ABC输出CBA。
栈的顺序储蓄实现:数组
栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成。故:
#define MaxSize <储存数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
ElementType Data[MaxSize];
int Top;
};
有关操作实现:
对于堆栈的有关操作有下面几种: //上图所示内容;
1、Stack CreateStack( int MaxSize ): 生成空堆栈,其最大长度为MaxSize;
2、int IsFull( Stack S, int MaxSize ):判断堆栈S是否已满;
3、void Push( Stack S, ElementType item ):将元素item压入堆栈;
4、int IsEmpty ( Stack S ):判断堆栈S是否为空;
5、ElementType Pop( Stack S ):删除并返回栈顶元素;
1.入栈:
void Push( Stack PtrS, ElementType item )
{
if ( PtrS->Top == MaxSize-1 )
{
printf(“堆栈满”); return;
}
else
{
PtrS->Data[++(PtrS->Top)] = item; //Date为一个数组;
return;
}
}
2.出栈:后入先出,将最顶端的元素输出;
ElementType Pop( Stack PtrS )
{
if ( PtrS->Top == -1 )
{
printf(“堆栈空”);
return ERROR; /* ERROR是ElementType的特殊值,标志错误*/
}
else
return ( PtrS->Data[(PtrS->Top)--] );
}
Example:用一个数组实现两个堆栈
一种比较聪明的方法是使这两个栈分别从数组的两头开始向中间生长。
#define MaxSize <存储数据元素的最大个数>
struct DStack {
ElementType Data[MaxSize];
int Top1; /* 堆栈1的栈顶指针 */
int Top2; /* 堆栈2的栈顶指针 */
} S;
S.Top1 = -1;
S.Top2 = MaxSize
当两个栈的栈顶指针相遇时,表示两个栈都满了。
//入栈:
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->Top1)] = item;
else /* 对第二个堆栈操作 */
PtrS->Data[--(PtrS->Top2)] = item;
}
//出栈:
ElementType Pop( struct DStack *PtrS, int Tag )
{ /* Tag作为区分两个堆栈的标志,取值为1和2 */
if ( Tag == 1 )
{ /* 对第一个堆栈操作 */
if ( PtrS->Top1 == -1 )
{ /*堆栈1空 */
printf(“堆栈1空”); return NULL;
}
else return PtrS->Data[(PtrS->Top1)--];
}
else
{ /* 对第二个堆栈操作 */
if ( PtrS->Top2 == MaxSize )
{ /*堆栈2空 */
printf(“堆栈2空”); return NULL;
}
else return PtrS->Data[(PtrS->Top2)++];
}
}
堆栈的链式存储实现:单向链表
首先一个问题,栈顶指针 Top 应该在哪一头?
答案自然是链表头结点,因为在链表头结点中存放有下个结点的地址,无论进行增加或删除操作都非常方便。链表尾结点则因未存放上一个链表的地址,而无法指向上一个结点,出现断链和野指针。
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 );
}
//入栈:
void Push( ElementType item, Stack S)
{ /* 将元素item压入堆栈S */
struct SNode *TmpCell;
TmpCell=(struct SNode *)malloc(sizeof(struct SNode));
TmpCell->Element = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
//出栈:
ElementType Pop(Stack S)
{ /* 删除并返回堆栈S的栈顶元素 */
struct SNode *FirstCell;
ElementType TopElem;
if( IsEmpty( S ) ) {
printf(“堆栈空”); return NULL;
} else {
FirstCell = S->Next;
S->Next = FirstCell->Next;
TopElem = FirstCell ->Element;
free(FirstCell);
return TopElem;
}
}
堆栈应用:表达式求值
从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
① 运算数:直接输出;
② 左括号:压入堆栈;
③ 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
④ 运算符: • 若优先级大于栈顶运算符时,则把它压栈;若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比 较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然 后将该运算符压栈;
⑤ 若各对象处理完毕,则把堆栈中存留的运算符一并输出。
附:中缀表达式和后缀表达式
中缀表达式:运算符号位于两个运算数之间。如 ,a + b * c - d / e
后缀表达式:运算符号位于两个运算数之后。如, a b c * + d e / -