栈
栈是一种先入先出的数据结构,插入和删除操作都只对最顶端的元素进行。
栈最重要的是如下几个操作:
- Push( ElementType X, Stack S ); //将一个元素压入栈中
- ElementType Top( Stack S ); //返回栈顶的元素值
- Pop( Stack S ); //将栈顶的元素出栈
其中,对一个空栈进行pop或pop操作,会引发数据结构栈的错误,这是不允许的。而对一个满了的栈进行push操作,是数据结构栈在实现上的错误,用数组实现可能出现的错误。
栈的链表实现
栈的链表实现,首先明白链表的头结点应该指向哪里?是指向栈的最底端还是最顶端。使用一个虚拟的头结点指向最顶端最为方便。如下图所示:
一个空栈,相当于一个仅含虚拟头节点的链表。
入栈,相当于在链表的第一个节点前面插入一个新节点
typedef struct Node *PtrToNode;
struct Node
{
int Data;
PtrToNode Next;
Node(int x) : Data(x),Next(nullptr) {}
Node():Next(nullptr){}
};
//链表实现,free与new are expensive
class Stack
{
private:
struct Node *S; //作为一个虚拟的头节点
public:
Stack()
{
S = new Node();
}
void Push(int X)
{
PtrToNode TmpCell = new Node(X);
TmpCell->Next = S->Next;
S->Next = TmpCell; //想象一下是在哪里加入
//是在S与S->Next这两个节点之间加入新节点
}
int IsEmpty()
{
return S->Next == nullptr;
}
int Top()
{
if (!IsEmpty())
{
return S->Next->Data; //S指向的是一个虚拟的头结点,不存任何值
}
else
{
std::cout << "Empty stack";
}
}
void Pop()
{
if (IsEmpty())
{
std::cout << "Empty stack";
}
else
{
PtrToNode FirstCell = S->Next; //暂存是为了能够释放内存
S->Next = S->Next->Next;
delete FirstCell;
}
}
};
栈的数组实现
push操作要检查数组是否溢出,pop操作要检查栈是否为空。
struct StackRecord
{
int Capacity; //数组初始化的大小,相当于栈的容量了
int TopOfStack; //指向栈顶的元素
int *Array; //数组
};
比较容易得到相应的操作代码:
入栈: S->Array[++S->TopOfStack] = X;
出栈:return S->Array[S->TopOfStack–];
返回栈顶元素:return S->Array[S->TopOfStack];
实现代码时注意考虑可能出现的错误!
struct StackRecord
{
int Capacity;
int TopOfStack;
int *Array;
};
class Stack_Array
{
struct StackRecord *S;
int Max;
public:
Stack_Array(int MaxElements)
{
S = new struct StackRecord;
Max = MaxElements;
if (S == nullptr)
std::cout << "out of space";
S->Array = new int[MaxElements];
if (S->Array == nullptr)
std::cout << "out of space";
S->Capacity = MaxElements;
MakeEmpty();
}
void MakeEmpty()
{
S->TopOfStack = -1;
}
int IsEmpty()
{
return S->TopOfStack == -1;
}
void DisposeStack()
{
if (S != nullptr)
{
delete[] S->Array;
delete S;
}
}
void Push(int X)
{
if (S->TopOfStack != Max - 1)
S->Array[++S->TopOfStack] = X;
else
std::cout << "full stack";
}
int Top()
{
if (S->TopOfStack != -1)
return S->Array[S->TopOfStack];
else
std::cout << "empty stack";
}
int Pop()
{
if (S->TopOfStack == -1)
std::cout << "empty stack";
else
return S->Array[S->TopOfStack--];
}
};
栈的应用
平衡符号(Balancing Symbols)
编译器对代码进行语法检查的时候,会检查出 [ ],(),{},这些有没有匹配。比如 [ ( ) ] 这样是合法的,而 [ ( ] )这样是不合法的。我们可以通过栈来实现这一个简单的功能。创建一个空栈,不断读入元素,读入的字符如果为(,{ 或 [ 就入栈,如果为),} 或 ] 就进行如下判断
- 此时栈为空,返回错误
- 栈不空的话,检查栈顶的元素与当前元素是否匹配,若不匹配,返回错误
- 若匹配,则将栈顶匹配的那个元素出栈
- 对所有字符都进行如上操作后,若栈不空,则返回错误
Algorithm {
Make an empty stack S;
while (read in a character c) {
if (c is an opening symbol)
Push(c, S);
else if (c is a closing symbol) {
if (S is empty) { ERROR; exit; }
else { /* stack is okay */
if (Top(S) doesn’t match c) { ERROR, exit; }
else Pop(S);
}
}
}
if (S is not empty) ERROR;
}