栈的简介
今天给大家介绍另一种常用的数据结构——栈。(注意,栈从不同的角度定义有不同的定义,从内存分配的角度定义是指变量由系统分配内存,今天讲的是一种数据结构)它是一种先进后出的结构即FILO(first in last out)。如下图所示,数据的插入(压栈)删除(出栈)都从栈顶操作。
栈通常具有以下接口:
- 判断栈是否为空
- 移除栈顶元素
- 在栈顶增加元素
- 返回栈中元素数目
- 返回栈顶元素
栈的应用十分的广泛,如逆序输出、数制转换和特殊符号匹配等,函数的调用就是很好的栈的应用,下面我们进行顺序栈和链栈的代码实现。
顺序栈
如上图所示,顺序栈使用数组存储数据,读取写入数据时用游标操作。空表时top=-1,表满时top=MAXSIZE-1,MAXSIZE为栈容量。
顺序栈类声明
声明时采用模版,可以适应处理多种类型数据。
#define MAXSIZE 10
template<typename T>
class stack
{
public:
stack() :_index(-1) {}; //初始化空表
~stack() {};
bool empty() const; //堆栈为空则返回真
void pop(); //移除栈顶元素
void push(T value); //在栈顶增加元素
int size() const; //返回栈中元素数目
T top() const; //返回栈顶元素
private:
int _index; //游标
T data[MAXSIZE]; //数组容量为MAXSIZE
};
顺序栈各接口实现
template<typename T>
inline bool stack<T>::empty() const
{
if (_index == -1)
return true;
return false;
}
template<typename T>
inline void stack<T>::pop()
{
if (_index == -1)
return;
_index--;
}
template<typename T>
inline void stack<T>::push(T value)
{
if (_index == MAXSIZE - 1)
return;
data[++_index] = value;
}
template<typename T>
inline int stack<T>::size() const
{
return _index + 1;
}
template<typename T>
inline T stack<T>::top() const
{
if (_index == -1)
return 0;
return data[_index];
}
顺序栈的操作实现较为简单,需要注意的是,在压栈和出栈时首先要判断栈是否空或满。
测试
int main()
{
stack<int> s;
int num[] = { 1,2,3,4,5,6 };
for (int i = 0; i < sizeof(num)/sizeof(int); i++)
{
s.push(num[i]);
}
cout << "栈容量:" << s.size() << endl;
while (!s.empty())
{
int value = s.top();
printf("%d ", value);
s.pop();
}
getchar();
}
输出:
压栈:1 2 3 4 5 6
栈容量:6
出栈:6 5 4 3 2 1
链栈
如图所示,链栈采用链式结构,每个节点包括一个数据元素和一个指向下一层节点的指针,栈底节点指针指向空。链栈没有最大容量限制。
链栈类声明
仍然使用模版。
/**********节点定义*********/
template<typename T>
struct ListNode
{
T m_nValue;
ListNode* m_pNext;
};
/**********链栈类**********/
template<typename T>
class LStack
{
public:
LStack();
~LStack();
private:
ListNode<T>* p_Top; //指向下一层节点指针
int _size; //节点数量
public:
bool empty() const; //堆栈为空则返回真
void pop(); //移除栈顶元素
void push(T value); //在栈顶增加元素
int size() const; //返回栈中元素数目
T top() const; //返回栈顶元素
};
链栈类实现
与顺序栈不同,链栈压栈时不需要判断栈是否满。
template<typename T>
LStack<T>::LStack() //构造空栈
{
_size = 0;
p_Top = nullptr;
}
template<typename T>
LStack<T>::~LStack()
{
}
template<typename T>
inline bool LStack<T>::empty() const
{
if (p_Top == nullptr)
return true;
return false;
}
template<typename T>
inline void LStack<T>::pop()
{
if (p_Top == nullptr) //判断栈是否空
return;
ListNode<T>* r = p_Top;
p_Top = p_Top->m_pNext;
delete r;
}
template<typename T>
inline void LStack<T>::push(T value)
{
ListNode<T>* r = new ListNode<T>;
r->m_nValue = value;
r->m_pNext = p_Top;
p_Top = r;
_size++;
}
template<typename T>
inline int LStack<T>::size() const
{
return _size;
}
template<typename T>
inline T LStack<T>::top() const
{
if (!p_Top)
return 0;
return p_Top->m_nValue;
}
测试
int main()
{
LStack<int> s;
int num[] = { 1,2,3,4,5,6 };
cout << "压栈:";
for (int i = 0; i < sizeof(num)/sizeof(int); i++)
{
s.push(num[i]);
cout << num[i] << " ";
}
cout << endl;
cout << "栈容量:" << s.size() << endl<<"出栈:";
while (!s.empty())
{
int value = s.top();
printf("%d ", value);
s.pop();
}
getchar();
}
输出:
压栈:1 2 3 4 5 6
栈容量:6
出栈:6 5 4 3 2 1
操作与效果和顺序栈一样。
总结
栈分为顺序栈和链栈,在需要逆序使用或操作数据时非常方便。但因为栈通常不处理特别大量的数据,所以栈通常是定长的即有最大容量。所以我们一般使用顺序栈。但理解链栈的实现也非常有必要。(最后声明本文所有图片皆引用网络)