C++中栈有顺序栈和链栈之分。
在顺序栈中,定义了栈的栈底指针(存储空间首地址base)、栈顶指针top以及顺序存储空间的大小stacksize
我就直接上代码看吧 顺序栈
#include<iostream>
#include<string.h>
using namespace std;
//typedef int Elemtype;
#define MAX 5
template<typename Elemtype>
class Cstack
{
public:
Cstack()
{
mbase=new (nothrow) Elemtype[MAX];
if(NULL==mbase)
return;
mtop=mbase;
mstacksize=MAX;
}
void Add_Volum(); //增加栈的容量
bool Is_Full();
bool Is_Empty();
void Push_Stack(Elemtype val)
{
if(Is_Full())
{
Add_Volum();
}
*mtop=val;
mtop++;
}
void Pop_Stack()
{
if(Is_Empty())
return;
mtop--;
}
Elemtype Get_Top()
{
if(Is_Empty())
{
cout<<"stack is empty"<<endl;
throw ""; //抛出一个异常 空字符串
}
return *(mtop-1);
}
void show()
{
if(Is_Empty())
return;
Elemtype *p=mbase;
cout<<"from base to top Elemtype is:";
while(p!=mtop)
{
cout<<*p<<" ";
p++;
}
cout<<endl;
}
void Clean_Stack()
{
mtop=mbase;
}
~Cstack()
{
cout<<"stack is over"<<endl;
delete []mbase;
mbase=NULL;
mtop=NULL;
}
private:
Elemtype *mbase; //栈底指针
Elemtype *mtop; //栈顶指针,指向最后一个元素的下一个位置
int mstacksize; //栈的容量
};
template<typename Elemtype>
void Cstack<Elemtype>::Add_Volum()
{
Elemtype *m=new (nothrow)Elemtype[2*mstacksize]; //2倍的容量扩大
if(NULL==m)
return;
memcpy(m,mbase,sizeof(Elemtype)*mstacksize);
mbase=m;
mtop=mbase+mstacksize;
mstacksize*=2;
}
template<typename Elemtype>
bool Cstack<Elemtype>::Is_Full()
{
if(mtop-mbase>=mstacksize)
return true;
return false;
}
template<typename Elemtype>
bool Cstack<Elemtype>::Is_Empty()
{
if(mtop==mbase)
return true;
return false;
}
int main()
{
Cstack<int> s;
for(int i=1;i<11;i++)
s.Push_Stack(i);
s.show();
cout<<"top Elemtype is:"<<s.Get_Top()<<endl;
s.Pop_Stack();
cout<<"Pop is over"<<endl;
s.show();
s.Clean_Stack();
s.show();
return 0;
}
下来我们看一看链栈的代码,你就可以看出区别
#include<iostream>
using namespace std;
template<typename Elemtype>
class CNode //节点类
{
private:
Elemtype mdata;
CNode *mnext;
template<typename T> //在Linux上不能写成Elemtype,与VS不同,会产生shadow
friend class Clinkstack;
public:
CNode()
{
mdata=0;
mnext=NULL;
}
CNode(Elemtype val)
{
mdata=val;
mnext=NULL;
}
};
template<typename Elemtype>
class Clinkstack
{
private:
CNode<Elemtype> *mbase; //头节点的指针
CNode<Elemtype> *mtop; //指向最后一个节点
public:
Clinkstack() //初始化头节点
{
mbase=new (nothrow) CNode<Elemtype>();
if(NULL!=mbase)
{
mtop=mbase;
}
}
~Clinkstack()
{
while(mtop!=mbase)
{
PopStack();
}
delete mbase;
mbase=NULL;
mtop=NULL;
cout<<"栈已摧毁"<<endl;
}
void PushStack(Elemtype val);
void PopStack();
CNode<Elemtype> *FindNode(); //找前趋节点
void ShowStack();
Elemtype GetTop();
bool IsEmpty()
{
if(mtop==mbase)
return true;
else
return false;
}
};
template<typename Elemtype>
void Clinkstack<Elemtype>::PushStack(Elemtype val)
{
CNode<Elemtype> *m=new (nothrow) CNode<Elemtype>(val);
if(NULL==m)
throw "";
mtop->mnext=m;
mtop=m;
}
template<typename Elemtype>
CNode<Elemtype>* Clinkstack<Elemtype>::FindNode() //找前趋
{
if(IsEmpty())
return mbase;
CNode<Elemtype> *m=mbase;
while(m->mnext!=mtop)
{
m=m->mnext;
}
return m;
}
template<typename Elemtype>
void Clinkstack<Elemtype>::PopStack()
{
if(IsEmpty())
return;
CNode<Elemtype> *m=mtop;
mtop=FindNode();
delete m;
m=NULL;
}
template<typename Elemtype>
void Clinkstack<Elemtype>::ShowStack()
{
if(IsEmpty())
return;
CNode<Elemtype> *m=mbase->mnext;
cout<<"从栈底到栈顶的元素依次是:";
while(m!=mtop)
{
cout<<m->mdata<<" ";
m=m->mnext;
}
cout<<m->mdata<<endl;
}
template<typename Elemtype>
Elemtype Clinkstack<Elemtype>::GetTop()
{
if(IsEmpty())
throw "";
return mtop->mdata;
}
int main()
{
Clinkstack<int> s;
// s.GetTop(); 测试异常
for(int i=0;i<10;i++)
s.PushStack(i+1);
s.ShowStack();
s.PopStack();
cout<<"一个元素出栈完成"<<endl;
s.ShowStack();
cout<<"栈顶元素是:"<<s.GetTop()<<endl;
return 0;
}
事实上这二者的差别是由顺序表和链表的存储结构决定的,在空间上,顺序表是静态分配的,而链表则是动态分配的可是链式表能够将非常多零碎的空间利用起来;顺序表查找方便。链式表插入和删除时非常方便。
另外,顺序栈和链栈的top指针有差别,顺序栈的top指针指向栈顶的空元素处,top-1才指向栈定元素,而链栈的top指针相当于链表的head指针一样,指向实实在在的元素。
队列(Queue)也是一种常见的线性表,它和栈相比有以下不同:
- 队列可以在表的两端进行操作。栈只能在栈顶进行插入和删除。
- 两端允许操作的类型不一样:可以进行删除的一端称为队头,这种操作也叫出队;可以进行插入的一端称为队尾,这种操作也叫入队。总的来说,队头只能出队,队尾只能入队。
队列的特点:先进先出(First in first out)或者后进后出(Last in last out)。
队列的示意图:
模拟队列这种数据结构并不是什么难事,但会遇到一些问题,如:
假溢出
队列中明明还有空间,却因为rear已经指向最后,造成无法入队问题,这是假溢出。
解决办法是:使用链式存储,这显然可以。在顺序存储时,我们常见的解决办法是把它首尾相接,构成循环队列,这可以充分利用队列的存储空间。
循环队列示意图:
在上图中,front指向队列中第一个元素,rear指向队列队尾的下一个位置。
但依然存在一个问题:当front和rear指向同一个位置时,这代表的是队空还是队满呢?大家可以想象下这种情景。
解决这种问题的常见做法是这样的:
- 使用一标记,用以区分这种易混淆的情形。
- 牺牲一个元素空间。当front和rear相等时,为空;当rear的下一个位置是front时,为满。
如下图:
下面我们给出循环队列,并采用第二种方式,即牺牲一个元素空间来区分队空和队满的代码.
几个重点:
- front指向队头,rear指向队尾的下一个位置。
- 出队时,front=(front+1)%MAXSIZE;入队时,rear=(rear+1)%MAXSIZE。
- 队为空的判断:front==rear;队为满的判断:(rear+1)%MAXSIZE==front。
接下来我们看看顺序栈的代码:
#include<iostream>
using namespace std;
#define MAX 10
template<typename Elemtype>
class Csqueue
{
public:
Csqueue();
~Csqueue();
void PushQueue(Elemtype val);
void PopQueue();
void AddVolum();
bool IsEmpty();
bool IsFull();
void clear();
Elemtype GetFront();
Elemtype GetRear();
void ShowQueue();
private:
Elemtype *mbase; //队列首地址
int mfront;
int mrear;
int squeuesize;
};
template<typename Elemtype>
Csqueue<Elemtype>::Csqueue()
{
mbase=new (nothrow)Elemtype[MAX];
if(NULL==mbase)
throw "";
mfront=mrear=0;
squeuesize=MAX;
}
template<typename Elemtype>
Csqueue<Elemtype>::~Csqueue()
{
delete []mbase;
}
template<typename Elemtype>
void Csqueue<Elemtype>::PushQueue(Elemtype val)
{
if(IsFull())
AddVolum();
*(mbase+mrear)=val;
mrear=(mrear+1)%squeuesize;
}
template<typename Elemtype>
void Csqueue<Elemtype>::PopQueue()
{
if(IsEmpty())
return;
mfront=(mfront+1)%squeuesize;
}
template<typename Elemtype>
void Csqueue<Elemtype>::AddVolum()
{
Elemtype *m=mbase;
mbase=new (nothrow)Elemtype[2*squeuesize];
if(mbase==NULL)
throw "";
memcpy(mbase,m,sizeof(Elemtype)*squeuesize);
squeuesize*=2;
delete []m;
}
template<typename Elemtype>
bool Csqueue<Elemtype>::IsEmpty()
{
if(mrear==mfront)
return true;
return false;
}
template<typename Elemtype>
bool Csqueue<Elemtype>::IsFull()
{
if((mrear+1)%squeuesize==mfront)
return true;
return false;
}
template<typename Elemtype>
void Csqueue<Elemtype>::clear()
{
mrear=mfront;
}
template<typename Elemtype>
Elemtype Csqueue<Elemtype>::GetFront()
{
if(IsEmpty())
throw "";
return *(mbase+mfront);
}
template<typename Elemtype>
Elemtype Csqueue<Elemtype>::GetRear()
{
if(IsEmpty())
throw "";
return mbase[(mrear-1+squeuesize)%squeuesize];
}
template<typename Elemtype>
void Csqueue<Elemtype>::ShowQueue()
{
if(IsEmpty())
{
cout<<"The queue is empty"<<endl;
return;
}
cout<<"from front to rear is: ";
int m=mfront;
while(m!=mrear)
{
cout<<mbase[m]<<" ";
m=(m+1)%squeuesize;
}
cout<<endl;
}
int main()
{
Csqueue<int> q;
for(int i=1;i<13;i++)
{
q.PushQueue(i);
}
q.ShowQueue();
cout<<"Pop a num"<<endl;
q.PopQueue();
q.ShowQueue();
cout<<"front num is:"<<q.GetFront()<<endl;
cout<<"rear num is:"<<q.GetRear()<<endl;
q.clear();
cout<<"clear is over"<<endl;
q.ShowQueue();
return 0;
}
这个代码有一点点错误,可能是因为模板有些小细节VS与Linux并不相同,这个代码在VS上执行是毫无问题的。。那位大侠看出来可以告诉本菜菜。。
下来我们看看链式队列,这个并不需要头节点,直接一个指向第一个元素和最后一个元素的指针进行操作。直接看吧。。
#include<iostream>
using namespace std;
template<typename Elemtype>
class Cnode
{
public:
Cnode(Elemtype val)
{
mdata=val;
mnext=NULL;
}
private:
Elemtype mdata;
Cnode<Elemtype> *mnext;
template<typename T>
friend class Clinkqueue;
};
template<typename Elemtype>
class Clinkqueue //不带头节点链表 rear指最后一个元素
{
public:
Clinkqueue():mfront(NULL),mrear(NULL){}
~Clinkqueue();
void PushQueue(Elemtype val);
void PopQueue();
Elemtype GetFront();
Elemtype GetRear();
bool IsEmpty();
void ShowQueue();
Cnode<Elemtype>* FindQueue(); //找尾节点的前趋节点
private:
Cnode<Elemtype> *mfront;
Cnode<Elemtype> *mrear;
};
template<typename Elemtype>
void Clinkqueue<Elemtype>::PushQueue(Elemtype val)
{
Cnode<Elemtype> *m=new (nothrow)Cnode<Elemtype>(val);
if(NULL!=m)
{
if(IsEmpty())
{
mfront=m;
mrear=m;
}
else
{
mrear->mnext=m;
mrear=m;
}
}
}
template<typename Elemtype>
void Clinkqueue<Elemtype>::ShowQueue()
{
if(IsEmpty())
{
cout<<"the queue is Empty"<<endl;
return;
}
Cnode<Elemtype> *m=mfront;
cout<<"from front to rear is:";
while(m!=mrear)
{
cout<<m->mdata<<" ";
m=m->mnext;
}
cout<<m->mdata<<endl;
}
template<typename Elemtype>
Clinkqueue<Elemtype>::~Clinkqueue()
{
while(!IsEmpty())
{
PopQueue();
}
cout<<"link is destoryed"<<endl;
}
template<typename Elemtype>
Cnode<Elemtype>* Clinkqueue<Elemtype>::FindQueue()
{
if(mrear==mfront)
return mrear;
Cnode<Elemtype> *m=mfront;
while(m->mnext!=mrear)
{
m=m->mnext;
}
return m;
}
template<typename Elemtype>
void Clinkqueue<Elemtype>::PopQueue()
{
if(IsEmpty())
return;
Cnode<Elemtype> *m=FindQueue();
if(m==mrear)
{
delete m;
mrear=NULL;
m=NULL;
mfront=NULL;
}
delete mrear;
mrear=m;
}
template<typename Elemtype>
Elemtype Clinkqueue<Elemtype>::GetFront()
{
if(IsEmpty())
throw "";
return mfront->mdata;
}
template<typename Elemtype>
Elemtype Clinkqueue<Elemtype>::GetRear()
{
if(IsEmpty())
throw "";
return mrear->mdata;
}
template<typename Elemtype>
bool Clinkqueue<Elemtype>::IsEmpty()
{
if(mfront==NULL||mrear==NULL)
return true;
return false;
}
int main()
{
Clinkqueue<int> q;
for(int i=1;i<=10;i++)
{
q.PushQueue(i);
}
q.ShowQueue();
q.PopQueue();
cout<<"Pop a num is:"<<endl;
q.ShowQueue();
cout<<"front num is:"<<q.GetFront()<<endl;
cout<<"rear num is:"<<q.GetRear()<<endl;
return 0;
}
以上就是所有的代码,可以看出链式的队列与链式栈结构很相似,而顺序栈与顺序队列结构又很相似。只要掌握原理,写起来真是很随意了。。。加油鸭