栈:栈是一种特殊的线型表,其插入(也称入栈或压栈)和删除(也称出栈和谈栈)操作都在表的同一端进行。这一端称为栈顶,另一端称为栈底。
其实栈就是一种被限制的线性表,栈的功能是之前线性表的子集,比如线性表能在任意地方进行插入,而栈只能在尾部进行插入。
接下来实现一个数组描述栈:
1.派生自之前的arrayList:
template<class T>
class stack
{
public:
virtual ~stack(){}
virtual bool empty() const=0;
virtual int size() const=0;
virtual T& top()=0;
virtual void pop()=0;
virtual void push(const T& theElement)=0;
};
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();}//因为对父类的const进行了重写,所以必须用作用域进行调用
int size() const{return arrayList<T>::empty();}
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;)}
};
当父类有自定义的有参构造函数,而你又没有定义无参的构造函数时,默认无参构造函数失效
子类构造函数是默认调用父类无参构造函数的,如果父类没有无参构造,你就得利用列表初始化显示调用
父类构造函数了。
派生出来的类性能低下。
2.重新写一个stack并不从arrayList派生
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 stack+1;}
T& top()
{
if(stackTop==-1)
throw stackEmpty();
return stack[stackTop];
}
void pop()
{
if(stackTop==-1)
throw stackEmpty();
stack[stackTop--].~T();//注意这里是先析构在减一。
}
void push(const T& theElement);
private:
int stackTop;
int arrayLength;
T *stack;
};
template<class T>
arrayStack::arrayStack(int initialCapacity)
{
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)
{
if(stackTop==arrayLength-1)
{
changeLength1D(stack,arrayLength,2*arrayLength)//这个倍增函数我们之前已经写过了
arrayLength*=2;
}
stack[++stackTop]=theElement;
}
这个构造的主要部分就是多了一个指向栈顶的int型变量,当栈为空时,栈顶指向-1,当增加一个数时,栈顶指向0,是很合理的。数据的存取都靠这个数据管理。
接下来是栈的链表实现,原理与上面一样,也是额外定义一个指向栈顶的元素。不过他的节点的next指针是指向之前的一个节点而不是之后的节点,因为它只能从后往前进行查找。
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++;//这个地方需要注意的是,chainNode里面的next指针指向的是上一个节点而不是后一个
}
private:
chainNode<T>* stackTop;
int stackSize;
}
template<class T>
linkedStack<T>::~linkedStack()
{
while(stackTop!=NULL)
{
chainNode<T>* nextNode=stackTop->next;
delete stackTop;
stackTop=nextNode;
}
}
template<class T>
void linkedStack<T>::pop()
{
if(stackSize==0)
throw stackEmpty();
chainNode<T>* nextNode=stackTop->next;//删除的时候通过next指针获取上一个节点的指针,然后删除当前节点
delete stackTop;
stackTop=nextNode;//再把上一个节点的指针赋值给stackTop指针。
stackSize--;
}