概述
在前一篇文章中,我们已经用数组实现了栈。在本篇文章中,我们将使用链表来实现栈。使用链表的优点是:动态增长,扩容时更加平滑。缺点是:略微复杂,需要额外管理所有节点。栈相关的操作,仍然是下面5个接口。
Push:向栈中添加一个元素。
Pop:从栈中移除顶部元素,并返回该元素。
Top:查看栈顶元素但不移除它。
IsEmpty:检查栈是否为空。
Size:获取栈中元素的数量。

实现原理
我们使用一个带头指针的单链表来实现栈。其中,栈顶对应链表的头部,栈底对应链表的尾部。有一个指针始终指向当前栈顶节点,即链表的第一个节点,每次插入/删除都在链表头部进行。这样做的好处是:所有栈操作只需要修改头指针即可,效率非常高。
在下面栈CLinkedListStack的实现中,我们声明了一个成员变量m_pTop。m_pTop代表栈顶,指向单链表的第一个节点。
cpp
class CLinkedListStack
{
public:
CLinkedListStack();
~CLinkedListStack();
void Push(int nValue);
int Pop();
int Top();
bool IsEmpty();
int Size();
private:
// 定义链表节点结构
struct Node
{
int nData;
struct Node* pNext;
};
Node* m_pTop;
};
Push操作
入栈就是在链表的头部插入一个新节点,详细步骤如下。
1、创建一个新节点。
2、新节点的pNext指向当前栈顶。
3、更新栈顶为新节点。
具体如何实现,可参考下面的示例代码。
cpp
void CLinkedListStack::Push(int nValue)
{
Node* pNode = new Node();
pNode->nData = nValue;
pNode->pNext = m_pTop;
m_pTop = pNode;
}
Pop操作
出栈就是删除链表头部节点,并返回其值,详细步骤如下。
1、如果栈为空,抛出异常。
2、取出栈顶节点的值,保存为临时变量。
3、将栈顶指针下移一位。
4、释放旧节点的内存。
5、返回上面的临时变量。
具体如何实现,可参考下面的示例代码。
cpp
int CLinkedListStack::Pop()
{
if (IsEmpty())
{
throw underflow_error("stack is empty");
}
Node* pTemp = m_pTop;
int nValue = pTemp->nData;
m_pTop = m_pTop->pNext;
delete pTemp;
return nValue;
}
完整实现
Top操作、IsEmpty操作、Size操作都比较简单,这里就不再赘述了。CLinkedListStack类的完整实现,可参考下面的示例代码。
cpp
#include <iostream>
#include <stdexcept>
using namespace std;
CLinkedListStack::CLinkedListStack() : m_pTop(NULL)
{
NULL;
}
CLinkedListStack::~CLinkedListStack()
{
while (!IsEmpty())
{
Pop();
}
}
void CLinkedListStack::Push(int nValue)
{
Node* pNode = new Node();
pNode->nData = nValue;
pNode->pNext = m_pTop;
m_pTop = pNode;
}
int CLinkedListStack::Pop()
{
if (IsEmpty())
{
throw underflow_error("stack is empty");
}
Node* pTemp = m_pTop;
int nValue = pTemp->nData;
m_pTop = m_pTop->pNext;
delete pTemp;
return nValue;
}
int CLinkedListStack::Top()
{
if (IsEmpty())
{
throw underflow_error("stack is empty");
}
return m_pTop->nData;
}
bool CLinkedListStack::IsEmpty()
{
return m_pTop == NULL;
}
int CLinkedListStack::Size()
{
int nSize = 0;
Node* pTemp = m_pTop;
while (pTemp != NULL)
{
++nSize;
pTemp = pTemp->pNext;
}
return nSize;
}
int main()
{
CLinkedListStack s;
s.Push(66);
s.Push(77);
s.Push(88);
cout << "Top element: " << s.Top() << endl;
cout << "Pop: " << s.Pop() << endl;
cout << "Pop: " << s.Pop() << endl;
cout << "Current size: " << s.Size() << endl;
cout << "Is empty: " << (s.IsEmpty() ? "True" : "False") << endl;
return 0;
}