二、棧
棧是一種“發育不良”的線性表,它具有與線性表相同的存儲結構(基於數組的或基於鏈於的),但棧的“缺陷”---不能像線性表那樣具有插入、刪除操作---反而給了它獨有的特色。在後面將會發現,遞歸,可以用棧來實現。
在時間複雜度上,基於數組的棧AStack和鏈式棧LStack,在push()、pop()操作上,都是一個時間常數1。在我的測試中,10萬次push()和10萬次pop()後,基於數組的棧只比鏈式棧快一秒。但要注意AStack需要預先指定棧元素的最大個數,而LStack是動態分配的,元素個數在理論上不受限。
經常思考,我發現昨天在線性表那篇文章中,有一個設計錯誤,就是不應該把結點類Node與List類放在一個文件中,這是違反模塊化思想的。當我今天的棧中要用到結點時,應該可以很輕鬆的通過頭文件的方式把結點類引到我的代碼中。以下是一個最簡單的結點類界面(事實上可以採用可用空間表freelist的方式來定義結點類):
定義常用於鏈表中的各種結點界面
文件名:NodeInterface.h
*/
#ifndef NODEINTERFACE_H
#define NODEINTERFACE_H
template<class T> class Node //單端結點,只含有一個指針
{
public:
T element;
Node * next;
public:
Node(const T &eVal, Node * nVal = NULL)
{
element = eVal;
next = nVal;
}
Node(Node * nVal = NULL)
{
element = 0;
next = nVal;
}
};
#endif
以下是棧的界面及兩種實現的代碼:
文件名:StackInterface.h
*/
#ifndef STACKINTERFACE_H
#define STACKINTERFACE_H
#include "NodeInterface.h"
//定義棧的界面
template<class T> class Stack
{
public:
virtual bool push(const T&) = 0; //入棧
virtual bool pop(T&) = 0; //出棧
virtual bool topValue(T&) =0; //棧頂元素值
};
/////////////////////////////////////
//基於數組的棧Array_based Satck
/////////////////////////////////////
template<class T> class AStack :public Stack<T>
{
private:
int maxSize; //棧最多能容納的元素個數
int top; //指示棧中元素的實際個數,由於數組從0開始,(top-1)才指示了棧頂元素的下標
T * listArray; //array holding stack elements
public:
AStack(int size = 100)
{
maxSize = size;
top = 0;
listArray = new T[maxSize];
}
~AStack()
{
delete [] listArray;
}
bool push(const T &);
bool pop(T &);
bool topValue(T &);
int length() const
{
return top;
}
};
template<class T> bool AStack<T>::push(const T & element)
{
if (top == maxSize) //stack is full.
{
return false;
}
listArray[top] = element;
++top;
return true;
}
template<class T> bool AStack<T>::pop(T & ref_elem)
{
if (top == 0) //no element in stack
{
return false;
}
ref_elem = listArray[--top];
return true;
}
template<class T> bool AStack<T>::topValue(T & ref_elem)
{
if (top == 0) //no element in stack
{
return false;
}
ref_elem = listArray[top-1];
return true;
}
///////////////////////////////////
//鏈式棧 Linked Stack
///////////////////////////////////
template<class T> class LStack :public Stack<T>
{
private:
int listSize; //the number of elements,and that is the number of nodes
Node<T> * top; //與AStack棧不同,這裏,top指向棧頂元素
public:
LStack() //不必限定元素的最大值
{
listSize = 0;
top = NULL;
}
~LStack()
{
}
bool push(const T &);
bool pop(T &);
bool topValue(T &);
void clear();
int length() const
{
return listSize;
}
};
template<class T> bool LStack<T>::push(const T &element)
{
Node<T> *tmp = new Node<T>(element,top);
top = tmp;
++listSize;
return true;
}
template<class T> bool LStack<T>::pop(T &ref_elem)
{
if (top == NULL)
{
return false; //棧中沒有元素
}
Node<T> *tmp = top;
top = top->next;
ref_elem = tmp->element;
delete tmp;
--listSize;
return true;
}
template<class T> bool LStack<T>::topValue(T & ref_elem)
{
if (top == NULL)
{
return false; //none element in stack
}
ref_elem = top->element;
return true;
}
template<class T> void LStack<T>::clear()
{
Node<T> * tmp;
while (top != NULL)
{
tmp = top;
top = top->next;
delete tmp;
}
}
#endif
晚一些時候,我把用棧實現遞歸的方法,寫到這裏來。