栈(stack)是限制插入和删除只在一个位置进行的表,该位置是表的末端,叫做栈顶。
栈的基本操作:进栈、出栈。
其他操作:返回栈顶元素、清空、判空。
数据结构与算法分析——C语言描述里有几句话不太明白,请路过的高手指教一下:
1、对空栈进行的Pop或Top一般认为是栈ADT的错误,当运行Push时空间用尽是个实现错误,但不是ADT错误。ADT错误和实现错误具体指什么,为什么Pop、Top属于ADT错误。
2、普通的清空栈的操作和判断是否空栈的测试都是栈的操作指令系统的一部分。为什么说是操作指令系统的一部分,应该是操作的一部分吧。
栈的实现:
1、使用链表(此链表带有头结点)
#ifndef _Stack_h
struct Node;
typedef struct Node *ptrToNode;
typedef PtrToNode Stack;
int IsEmpty(Stack S);
Stack CreateStack(void);
void MakeEmpty(Stack S);
void Push(ElementType X, Stack S);
ElementType Top(Stack S);
void Pop(Stack S);
#endif /*_Stack_h*/
/* Place in implementation file*/
/*Stack implementation is a linked list with a header*/
struct Node
{
<span style="white-space:pre"> </span>ElementType Element;
<span style="white-space:pre"> </span>PtrToNode Next;
}
下面一一实现栈操作:
int IsEmpty(Stack S)//返回类型可更改为bool类型
{
<span style="white-space:pre"> </span>return S -> next == NULL; // S == NULL;
}
<pre name="code" class="cpp">Stack CreateStack(void)
{
Stack head = malloc(sizeof(stuct Node)); //Stack head = NULL; return head;
if (head == NULL)
<span style="white-space:pre"> </span>FatalError("Out of Space!!!");//涉及到异常的处理,以后再探讨异常
head->Next = NULL;
<span style="white-space:pre"> </span>MakeEmpty(head);//不明白这里加个清空有什么作用
<span style="white-space:pre"> </span>return head;
}
<pre name="code" class="cpp">void MakeEmpty(Stack S)
{
<span style="white-space:pre"> </span>if (S == NULL)
<span style="white-space:pre"> </span>Error("Must use CreateStack first");
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>while (!IsEmpty(S))
<span style="white-space:pre"> <span style="white-space:pre"> </span></span>Pop(S);
}
void Push(ElementType X, Stack S)
{
<span style="white-space:pre"> </span>PtrToNode TmpCell;
<span style="white-space:pre"> </span>TmpCell = malloc (sizeof (struct Node));
<span style="white-space:pre"> </span>if (TmpCell == NULL)
<span style="white-space:pre"> </span>FatalError("Out of space!!!");
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{<span style="white-space:pre"> </span>TmpCell -> Element = X;
<span style="white-space:pre"> </span>TmpCell -> Next = S -> Next;
<span style="white-space:pre"> </span>S -> Next = TmpCell;
<span style="white-space:pre"> </span>}
}
ElementType Top(Stack S)
{
<span style="white-space:pre"> </span>if (S -> Next)
<span style="white-space:pre"> </span>return S -> Next -> ElementType;
<span style="white-space:pre"> </span>Error("Empty Error");
<span style="white-space:pre"> </span>return 0;
}
void Pop(Stack S)
{
<span style="white-space:pre"> </span>PtrToNode FirstCell;
<span style="white-space:pre"> </span>if (IsEmpty(S))
<span style="white-space:pre"> </span>Error("Empty stack");
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>FirstCell = S-> Next;
<span style="white-space:pre"> </span>S -> Next = S -> Next -> Next;
<span style="white-space:pre"> </span>free(FirstCell);
<span style="white-space:pre"> </span>}
}
有兴趣的同学可以尝试使用不带头结点的链表实现栈。
链表实现栈操作缺点:对于malloc和free的调用开销是昂贵的。
解决缺点的方法:使用两个栈,将不用的空间放入第二个栈,需要用空间时,到有空间的第二个栈中取。
2、使用数组
暂不详细。
二、栈的应用
栈最典型的应用为四则元素和函数调用
1、中缀表达式变成后缀表达式
遇到操作数直接输出,遇到操作符压入栈中,在压入栈前需要把优先级更高的操作符出栈输出
2、计算后缀表达式