栈和队列笔记

 

一、特殊的线性表——栈

1.栈:限定仅在表尾进行插入和删除操作的线性表。

栈的特性:先进后出(first in last out);允许删除和插入的一端称为栈顶,另一端称为栈底;插入包括入栈、进栈、压栈,删除包括出栈、弹栈。
注:栈只是对表插入和删除操作的位置进行了限制,并没有限定插入和删除操作进行的时间。
2.栈的抽象数据类型定义:

DestroyStack
     前置条件:栈已存在
     输入:无
     功能:销毁栈
     输出:无
     后置条件:释放栈所占用的存储空间
Push 
     前置条件:栈已存在
     输入:元素值x
     功能:在栈顶插入一个元素x
     输出:如果插入不成功,抛出异常
     后置条件:如果插入成功,栈顶增加了一个元素
Pop 
    前置条件:栈已存在
    输入:无
    功能:删除栈顶元素
    输出:如果删除成功,返回被删元素值,否则,抛出异常
    后置条件:如果删除成功,栈减少了一个元素
GetTop
    前置条件:栈已存在
    输入:无
    功能:读取当前的栈顶元素
    输出:若栈不空,返回当前的栈顶元素值
    后置条件:栈不变
Empty 
    前置条件:栈已存在
    输入:无
    功能:判断栈是否为空
    输出:如果栈为空,返回1,否则,返回0
    后置条件:栈不变
endADT

(一)、栈的顺序存储结构与实现

1.顺序栈:栈的顺序存储结构。

通常把数组中下标为0的一端作为栈底,同时附设变量top指示栈顶元素在数组中的位置。栈空时:top=-1,栈满时:top=StackSize-1;入栈:top加1;出栈:top减1。

2. 顺序栈的实现:将栈的抽象数据类型定义在顺序栈存储结构下用c++的类实现。成员变量实现顺序栈的存储结构,成员变量实现栈的基本操作。

顺序栈类的声明:

const int StackSize=100;//根据实际问题具体定义
template  <class T>
class  seqStack
{
    public:
        seqStack ( ) ;//构造函数,初始化一个空栈
        ~seqStack ( );//析构函数
        void  Push ( T  x );//入栈操作,将元素x入栈
        T   Pop ( );//出栈操作,将栈顶元素弹出
        T   GetTop ( );//取栈顶元素(并不删除)
        bool  Empty ( );//判断站是否为空
    private:
        T  data[MAX_SIZE];//存放栈元素的数组
        int  top;//栈顶元素在数组中的下标
}

(1)构造函数——顺序栈的初始化:初始化一个空的顺序栈只需将栈顶指针top置为-1。

(2)析构函数——顺序栈的销毁:顺序栈是静态存储分配,在顺序栈变量退出作用域时自动释放顺序栈所占存储空间,因此顺序栈无须销毁,析构函数为空。

(3)入栈操作

template <class T>
void  seqStack<T>::Push ( T  x)
{
     if (top==MAX_SIZE-1)  throw  “溢出”;
     data[++top]=x;//top++;data[top]=x;(注意top++和++top的区别)
 }

(4)出栈操作

template  <class  T>
T  seqStack<T>:: Pop ( )
{
     if (top==-1)  throw  “溢出”;
     x=data[top];
      top--;
     return  x;
}
(5)取栈顶元素

template <class T>
T  seqStack<T>::GetTop ( )
{
     if (Empty()) throw  ”空栈” ;
     return data[top];
 }
(6)判断是否为空

template <class T>
bool  seqStack<T>::Empty ()
{
     if (top==-1) 
         return true;
     return false;
 }
★两栈共享空间(双端栈):使用一个数组来存储两个栈,让一个栈的栈底为该数组的始端,另一个栈的栈底为该数组的末端,两个栈从各自的端点向中间延伸。

栈1为空时:top=-1;栈2为空时:top=Stack_Size;栈满:top1=top2-1。

--两栈共享空间类的声明:

const int Stack_Size=100;  
template <class T>
class BothStack 
{
  public:
       BothStack( );
       ~BothStack( ); 
       void Push(int i, T x);   
       T Pop(int i);          
       T GetTop(int i);       
       bool Empty(int i);     
  private:
       T data[Stack_Size];     
       int top1, top2;        
};

--两栈共享空间的实现——插入

1. 如果栈满,则抛出上溢异常;
2. 判断是插在栈1还是栈2;
       2.1 若在栈1插入,则
             2.1.1 top1加1;
             2.1.2 在top1处填入x;
       2.2 若在栈2插入,则
             2.2.1 top2减1;
             2.2.2 在top2处填入x;

template <class T> 
void BothStack<T>::Push(int i, T x )
{
    if (top1==top2-1) //判断是否栈满
		throw "上溢";
    if (i==1) 
		data[++top1]=x;
    if (i==2) 
		data[--top2]=x;
}

--两栈共享空间的实现——删除

1.若是在栈1删除,则
    1.1 若栈1为空栈,抛出下溢异常;
    1.2 删除并返回栈1的栈顶元素;
2.若是在栈2删除,则
     2.1 若栈2为空栈,抛出下溢异常;
     2.2 删除并返回栈2的栈顶元素;

 

 

template <class T>
T BothStack<T>::Pop(int i){
    if (i==1) {   
        if (top1== -1) 	throw "下溢";
        return data[top1--];
    }
    if (i==2) {                           
        if (top2==StackSize)  	throw "下溢";
        return data[top2++]; 
   }
}

--判断某个栈空的算法:

template <class T> 
bool BothStack<T>::Empty(int i)
{  
    if(i==1){
        if(top1==-1) 	return 1;
        else 	return 0;
    }
    if(i==2)	{
        if(top2==StackSize)	return 1;
        else  return 0;
    }
}

--取某个栈栈顶的算法:

template <class T> 
T BothStack<T>::GetTop(int i)
{
    if(i==1)	{
        if (top1!=-1) 	return data[top1];
    }
    if(i==2)	{
        if(top2!=StackSize)  return data[top2];
   } 
}

(二)、栈的链式存储结构及实现

1.链栈:栈的链接存储结构。将链头作为栈顶,方便操作;链栈不需要附设结点。

2.链栈的类声明:

template <class T>
class LinkStack
{    
   public:
         LinkStack( ) {top=NULL;}//构造函数,初始化一个空链栈
         ~LinkStack( );//析构函数          
         void Push(T x); //入栈操作,将元素x入栈
         T Pop( ); //出栈操作,将栈顶元素出栈
         T GetTop( );//取栈顶元素(并不删除)
         bool Empty( );//判断链栈是否为空
   private:
         Node<T> *top; //栈顶指针即链栈的头指针
}

(1)链栈的实现——插入(入栈)(没有判断栈满)

template <class T>
void LinkStack<T>::Push(T x)
{
    s=new Node<T>;
    s->data=x; 
    s->next=top; 
   top=s; 
}

(2)删除(出栈)

template <class T>
T LinkStack<T>::Pop( )
{
    if (top==NULL)
     throw "下溢";
    x=top->data; 
    p=top; 
    top=top->next; //不能用top--  
    delete p;
    return x;
}

(3)析构(链栈的销毁)

template <class T>
LinkStack<T>::~LinkStack( )
{
	while (top)
	{
		Node<T> *p;
		p=top->next;
        	delete top;
		top=p;
	}
}

(三)、顺序栈和链栈的比较

1.时间性能:相同,都是常数时间O(1)。

2.空间性能:
1)顺序栈:有元素个数的限制和空间浪费的问题。

链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满,但是每个元素都需要一个指针域,从而产生了结构性开销。

2)结论:当栈的使用过程中元素个数变化较大时,用链栈是适宜的,反之,应该采用顺序栈。
二、特殊线性表——队列

队列是只允许在一端进行插入操作,而另一端进行删除操作的线性表;队列的特性:先进先出。允许插入(也称入队、进队)的一端称为队尾,允许删除(也称出队)的一端称为队头。不含任何数据元素的队列称为空队列。

(一)、顺序队列:队列的顺序存储结构

1.队头指针和队尾指针

1)、队头指针指向队列中的第一个元素之前的元素,队尾指针指向队列中的最后一个元素

2)、队头指针指向队列中的第一个元素,队尾指针指向队列中的最后一个元素的后一个位置

假溢出:当元素被插入到数组中下标最大的位置上之后,队列的空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做假溢出。
解决假溢出——循环队列

2.循环队列:将存储队列的数组头尾相接。

3.如何确定不同的队空、队满的判定条件?

方法一:附设一个存储队列中元素个数的变量num,当num=0时队空,当num=QueueSize时为队满;
方法二:修改队满条件,浪费一个元素空间,队满时数组中只有一个空闲单元;
方法三:设置标志flag,当front=rear且flag=0时为队空,当front=rear且flag=1时为队满。

队满的条件:(rear+1) %QueueSize==front;队空:rear==front.
4.循环队列类的声明:

const int QueueSize=100; 
template <class T>    
class CirQueue{ 
  public:
      CirQueue( ); 
      ~ CirQueue( );
      void EnQueue(T x); 
     T DeQueue( );             
     T GetQueue( ); 
     bool Empty( ){
      if (rear==front) return true;
       return false;
   };
  private:
     T data[QueueSize];   
     int front, rear;
};

(1)入队  

template <class T>
 void CirQueue<T>::EnQueue(T x)
 {
   if ((rear+1) % QueueSize ==front) throw "上溢";
   rear=(rear+1) % QueueSize;   
   data[rear]=x;                
 }
(2)出队

template <class T>
T CirQueue<T>::GetQueue( )
{
    if (rear==front) throw "下溢";
    i=(front+1) % QueueSize; 
    return data[i];
}
(3)队列长度

template <class T>
int CirQueue<T>::GetLength( )
{
    if (rear==front) throw "下溢";
    len=(rear-front+ QueueSize) % QueueSize; 
    return len;
}
(二)队列的链接存储结构及实现

1.链队列:队列的链接存储结构。队头指针(front)即链表的头指针(frist)。

2.链队列类的声明:

template <class T>
class LinkQueue
{  
  public:
      LinkQueue( );    
      ~LinkQueue( );
      void EnQueue(T x);
      T DeQueue( );      
      T GetQueue( );
      bool Empty( );
  private:
      Node<T> *front, *rear;
};
(1)构造函数

template <class T>
LinkQueue<T>::LinkQueue( )
{
     front=new Node<T>;
     front->next=NULL; 
     rear=front;
}
(2)入队

 

template <class T>
void LinkQueue<T>::EnQueue(T x)
{
    s=new Node<T>;
    s->data=x;
    s->next=NULL;
    rear->next=s;
    rear=s;
}
(3)出队
template <class T>
T LinkQueue<T>::DeQueue( )
{
     if (rear==front) throw "下溢";
     p=front->next;
     x=p->data;
     front->next=p->next;       
     delete p;
     if (front->next==NULL) rear=front;  
     return x;
}
(三)循环队列和链队列的比较
时间性能:循环队列和链队列的基本操作都需要常数时间O (1)。
空间性能:
循环队列:必须预先确定一个固定的长度,所以有存储元素个数的限制和空间浪费的问题。
链队列:没有队列满的问题,只有当内存没有可用空间时才会出现队列满,但是每个元素都需要一个指针域,从而产生了结构性开销。
 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值