栈
一、定义
定义:
栈(stack)是一种特殊的线性表,其插入(也称为入栈或压栈)和删除(也成为出栈或弹栈)操作都在表的同一端进行。这一端称为栈顶(top),另一端成为栈底(bottom)。可以使用前面实现过的基于数组和链表的线性表实现栈结构。在计算过程中经常使用。
现实中的应用:
- 打印机纸盘
- 自助餐厅的盘子等
抽象数据类型:
template<class T>
class stack
{
public:
virtual ~stack() {}
virtual bool empty() const = 0;
// return true if stack is empty
virtual int size() const = 0;
// return number of elements in stack
virtual T& top() = 0;
// return reference to the top element
virtual void pop() = 0;
// remove the top element
virtual void push(const T& theElement) = 0;
// insert theElement at the top of the stack
};
二、数组描述
栈的插入和删除都被限制在一端进行,因此可以选择数组的左端或右端定义为栈顶。而数组尾端操作效率较高(从右端插入和删除不需要移动其他元素),可将右端定义为栈顶。
1. 使用arrayList派生类实现
template<class T>
class derivedArrayStack : private arrayList<T>, public stack<T>
{
public:
derivedArrayStack(int initialCapacity = 10): arrayList<T> (initialCapacity) {}
bool empty() const{
return arrayList<T>::empty();
}
int size() const{
return arrayList<T>::size();
}
T& top(){
if (arrayList<T>::empty())
throw stackEmpty();
return get(arrayList<T>::size() - 1);
}
void pop()
{
if (arrayList<T>::empty())
throw stackEmpty();
erase(arrayList<T>::size() - 1);
}
void push(const T& theElement)
{insert(arrayList<T>::size(), theElement);}
};
评论:
虽然上述方法能很简单的实现栈,但会带来性能上的损失。例如当往栈中插入一个元素是,push方法会调用arrayList 的insert方法,插入元素前要对下标进行检查,可能要将数组加长,而且还要往回复制。但下标检查和往回复制都是不必要的,因为我们总是将新元素插到最右端。
因此,可用数组实现性能更好的栈。
三、srrayStack类
template<class T>
class arrayStack : public stack<T>
{
public:
arrayStack(int initialCapacity = 10);
~arrayStack() {delete [] stack;}
bool empty() const {return stackTop == -1;}
int size() const
{return stackTop + 1;}
T& top()
{
if (stackTop == -1)
throw stackEmpty();
return stack[stackTop];
}
void pop()
{
if (stackTop == -1)
throw stackEmpty();
stack[stackTop--].~T(); // destructor for T
}
void push(const T& theElement);
private:
int stackTop; // current top of stack
int arrayLength; // stack capacity
T *stack; // element array
};
template<class T>
arrayStack<T>::arrayStack(int initialCapacity)
{// Constructor.
if (initialCapacity < 1)
{ostringstream s;
s << "Initial capacity = " << initialCapacity << " Must be > 0";
throw illegalParameterValue(s.str());
}
arrayLength = initialCapacity;
stack = new T[arrayLength];
stackTop = -1;
}
template<class T>
void arrayStack<T>::push(const T& theElement)
{// Add theElement to stack.
if (stackTop == arrayLength - 1)
{// no space, double capacity
changeLength1D(stack, arrayLength, 2 * arrayLength);
arrayLength *= 2;
}
// add at stack top
stack[++stackTop] = theElement;
}
四、链表描述
同样的可以使用链表描述栈。使用左端作为栈顶的性能要优于使用右端作为栈顶。因此,将链表的左端作为栈顶。
linkedStack类实现:
template<class T>
class linkedStack : public stack<T>
{
public:
linkedStack(int initialCapacity = 10)
{stackTop = NULL; stackSize = 0;}
~linkedStack();
bool empty() const
{return stackSize == 0;}
int size() const
{return stackSize;}
T& top()
{
if (stackSize == 0)
throw stackEmpty();
return stackTop->element;
}
void pop();
void push(const T& theElement)
{
stackTop = new chainNode<T>(theElement, stackTop);
stackSize++;
}
private:
chainNode<T>* stackTop; // pointer to stack top
int stackSize; // number of elements in stack
};
template<class T>
linkedStack<T>::~linkedStack()
{// Destructor.
while (stackTop != NULL)
{// delete top node
chainNode<T>* nextNode = stackTop->next;
delete stackTop;
stackTop = nextNode;
}
}
template<class T>
void linkedStack<T>::pop()
{// Delete top element.
if (stackSize == 0)
throw stackEmpty();
chainNode<T>* nextNode = stackTop->next;
delete stackTop;
stackTop = nextNode;
stackSize--;
}
五、性能对比:
数组描述的性能要好于链表描述的栈。
原因:
- 数组的空间分配是一组一组进行的;而链表的空间分配是一个节点一个节点进行的。
- 从占用内存空间上看:链表中每个节点还要保存指向下一个元素的指针,所需要分配的空间也相对更大。