【数据结构复习】第三章栈和队列

栈和队列

一、栈

1.栈的相关定义

(1)是限定在仅在表的一端进行插入和删除操作的线性表。
允许插入和删除的一端称为栈顶,另一端为栈底,不含任何数据元素的栈成为空栈
(2)插入元素叫入栈,删除元素叫出栈
(3)栈中元素的特性——后进先出

2.栈的顺序存储结构(顺序栈)
数组下标为0的一端表示栈底top表示栈顶元素在数组中的位置。

设存储栈数组长度StackSize,栈空时top=-1, 栈满时top=StackSize-1。(注意数组下标从0开始)
在这里插入图片描述

3.顺序栈的实现
(1)声明

const int StackSize = 10;
template <typename DataType>
class StackSize
{
public:
    SeqStack();                 //构造函数,初始化一个空栈
    ~SeqStack();
    void Push(DataType x)       //入栈操作,将元素x入栈
    DataType Pop();             //出栈操作,将栈顶元素谈弹出
    int Empty();                //判断栈是否为空

private:
    DataType data[StackSize];   //存放栈元素的数组
    int top;                    //栈顶元素在数组的下标(即定义栈的指针top)
};

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

template <typename DataType>
SeqStack::SeqStack()
{
    top = -1;
}

(3)析构函数

template <typename DataType>
SeqStack::~SeqStack()
{
}

(4)入栈操作
在栈中插入元素x,只需将栈顶位置top+1,然后在top的位置填入元素x


template <typename DataType>
void SeqStack<DataType>::Push(DataType x)
{
    if (top == StackSize-1) throw"上溢";
    data[++top] = x;			//top先自增移动top再取x的值
}

注:++top是先自增再取值,top++是先取值再自增。

(5)出栈操作
取出栈顶元素,top-1;


template <typename DataType>
DataType SeqStack<DataType>::Pop()
{
    DataType x;
    if (top==-1) throw"下溢";
    x = data[top--];            //先把元素的值赋值给x,top再-1
    return x;
}

(6)取栈顶元素


template <typename DataType>
DataType SeqStack<DataType>::GetTop()
{
    if(top==-1)
        throw"下溢异常";
    else
        return data[top];
}

(7)判空操作

template <typename DataType>
int SeqStack<DataType>::Empty()
{
    if(top==-1)
        return 1;
    else 
        return 0;
}

4.链栈的相关定义

栈的链接存储结构称为链栈,常用单链表表示。
注:有指针指向的一端作为栈顶。
在这里插入图片描述

5.链栈的实现

(1)初始化

template <typename DataType>
class StackSize
{
public:
    LinkStack();                //初始化空链栈
    ~LinkStack();
    void Push(DataType x)       //入栈,将元素x入栈
    DataType Pop();             //出栈,将栈顶元素取出
    int Empty();
private:
    Node<DataType> *top;        //栈顶指针即链栈的头指针
};

(2)初始化
由于链栈不带头结点,初始化一个空链栈只需将栈顶指针top置空

template <typename DataType>
LinkStack::LinkStack()
{
    top = new Node;             //生成头结点
    top->next = nullptr;        //将头结点的指针域置空
}

(3)析构——链栈销毁

template <typename DataType>
LinkStack::~LinkStack()
{
    Node *q = nullptr;
    while(top!=nullptr)         //释放链栈的每一个结点的存储空间
    {
        q = top;                 //暂存被释放结点
        top = top->next;         //top指向被释放的下一个结点
        delete q;
    }
}

(4)入栈操作
只需关注栈顶
在这里插入图片描述

template <typename DataType>
void LinkStack<DataType>::Push(DataType x)
{
    Node<DataType> *s = nullptr;
    s = new Node<DataType>;
    s->data = x;                //数据域
    s->next = top;              //将结点s插到栈顶
}

(5)出栈操作
只需处理栈顶
在这里插入图片描述

template <typename DataType>
DataType LinkStack<DataType>::Pop()
{
    Node<DataType> *p = nullptr;
    DataType x;
    if (top==nullptr) throw"下溢";
    x = top->data; p = top;         //暂存栈顶元素
    top = top->next;                //将栈顶元素摘链
    delete p;                       //删除暂存元素
    return x;
}

(6)取栈顶元素
只需返回栈顶指针所指结点的数据域,不用修改栈顶指针

template <typename DataType>
DataType LinkStack<DataType>::GetTop()
{
    if(top==nullptr)
        throw"下溢异常";
    else
        return top->data;
}

(7)判空操作
判断top是否为空

template <typename DataType>
int LinkStack::Empty()
{
    if(top==nullptr)
        throw"下溢异常";
    else
        return 0;
}

二、队列

1.队列相关定义
(1)队列只允许在一端进行插入,在另一端进行删除操作的线性表
(2)允许插入的一端称为队尾,允许删除的一端称为队头
(3)队列中的元素除具有线性关系外,还具有先进先出的特性。
在这里插入图片描述

2.顺序队列的存储结构
(1)队列的顺序结构称为顺序队列
(2)队列元素必须存储在前n个单元的入队出队:
队头元素放在数组下标为0的一端,入队相当于追加不需移动元素。

(3)去掉前n个单元条件的入队出队:
设置队头、队尾两个位置变量front指向队头元素前一个位置,rear指向队尾元素的位置
队头队尾都能活动,入队时rear+1,出队时front+1。
在这里插入图片描述

3.循环队列的存储结构
(1)假溢出:随队列的插入删除,队列向数组的高端移动,产生单向移动性,当元素插入到数组下标最大位置后,数组空间用尽低端有空间也不能用,这种现象叫假溢出
在这里插入图片描述
(2)解决假溢出的方法:将存储队列的数组看成首位相接的循环结构,允许队列从数组下标最大的位置延续到最小的位置。(通过取模操作实现)
设存储队列的数组长度为QueueSize,则操作语句为rear=(rear+1)%QueueSize,这种首尾相接的循环结构叫循环队列。(一开始rear在QueueSize-1的位置,再+1取余使rear到0的位置)

(3)循环队列的判空、判满
队空条件:front = rear
在这里插入图片描述

队满条件:(rear+1)%QueueSize = front牺牲1个数组元素空间使队尾与队头差1
在这里插入图片描述

4.循环队列的实现
(1)声明

const int QueueSize = 100;
template<typename DataType>     //定义模板类CirQueue
class CirQueue
{
public:
    CirQueue();                     //初始化空队列
    ~CirQueue();
    void EnQueue(DataType x)        //入队操作,将元素x入队
    DataType DeQueue();             //出队操作,将队头元素出队
    DataType GetHead();             //取队头元素,不删除
    int Empty();                    
private:
    DataType data[QueueSize];       //存放队头元素的数组
    int front,rear;                //队头队尾指针
};

(2)构造函数——循环队列初始化
初始化空的队列只需将队头front和队尾rear同时指向数组的某一位置,一般为数组高端即
rear = front = QueueSize-1

template <typename DataType>
CirQueue::CirQueue()
{
    rear = front =QueueSize-1;
}

(3)析构函数
退出作用域时自动释放,函数为空

template <typename DataType>
CirQueue::~CirQueue()
{

}

(4)入队操作
将队尾位置rear在循环意义下+1,再将待插元素x插入队尾。

template <typename DataType>
void CirQueue<DataType>::EnQueue(DataType x)
{
    if ((rear+1)%QueueSize==front) throw"上溢";      //队满
    rear = (rear+1)%QueueSize;                      //队尾指针在循环意义下+1
    data[rear] = x;                                 //在队尾处插入元素x
}

(5)出队操作
将队头位置front在循环意义下+1,读取并返回队头元素。

template <typename DataType>
DataType CirQueue<DataType>::GetHead()
{
    if (rear==front) throw"下溢";         //队空
    front = (front+1)%QueueSize;          //队头元素在循环意义下+1
    return data[front];                   //返回出队前队头的元素
}

(6)取队头元素
与出队类似,不用改变队头的位置

template <typename DataType>
DataType CirQueue<DataType>::GetHead()
{
    if (rear==front) throw"下溢";         //队空
    return data[(front+1)%QueueSize];     //注意不修改队头指针的位置
}

(7)判空操作
只需判断front 是否=rear

template <typename DataType>
{
	if(rear==front)
		throw"下溢";
	else
		return 0;
}

5.队列的链接存储
(1)队列的链接存储叫链队列,常用单链表表示。
(2)设置队头指针指向头结点,队尾指针指向终端结点。
在这里插入图片描述

6.链队列的实现
(1)声明

template <typename DataType>
class LinkQueue
{
 public: 
    LinkQueue();                //初始化空的链队列
    ~LinkQueue();
    void EnQueue(DataType x)    //入队,将元素x入队
    DataType DeQueue();         //出队,队头元素出队
    DataType GetHead();         //取链队列队头元素
    int Empty();
 private:
    Node<DataType>*front,*rear; //队头和队尾指针
};

(2)构造函数——链队列的初始化
申请头结点,让队头指针和队尾指针都指向头结点

template <typename DataType>
LinkQueue<DataType>::LinkQueue()
{
    Node<DataType>*s = nullptr;
    s = new Node<DataType>; s->next = nullptr;
    front = rear = s;                   //将队头指针队尾指针都指向头结点s
}

(3)析构函数——链队列的销毁
与单链表类似

template <typename DataType>
LinkQueue<DataType>::~LinkQueue()
{
    Node<DataType>*p = front;
	while(front!==nullptr)
	{
	    front = front->next;
        delete p;
        p = front;
	}
}

(4)入队操作
插入操作只在链表尾部进行,链队列带头结点,空链队列和非空链队列操作语句一致。
在这里插入图片描述

template <typename DataType>
void LinkQueue<DataType>::EnQueue(DataType x)
{
    Node<DataType>*s = nullptr;
    s = new Node<DataType>;             //申请结点s
    s->data = x; s->next = nullptr;		//s的数据域为x,s的next为空
    rear->next = s; rear = s;           //将结点s插入到队尾
}

方法二
不带头结点的链队列入队操作,空链队列和非空链队列操作语句不同。
空链入队时front需要指向s
在这里插入图片描述

(5)出队操作
链队列的删除操作只考虑在链表头部进行,注意队列长度=1的特殊情况
在这里插入图片描述

template <typename DataType>
DataType LinkQueue<DataType>::DeQueue()
{
    DataType x;
    Node<DataType>*p = nullptr;
    if (rear==front) throw"下溢";
    p = front->next; x = p->data;           //暂存队头元素
    front->next=p->next;                    //将队头元素所在结点摘链
    if (p->next==nullptr) rear = front;    //出队前队列长度为1
    delete p;
    return x;
}

(6)取队头元素
取队头元素只需返回第一个数据元素结点的数据域
front->next->data

template <typename DataType>
DataType LinkQueue::GetQueue()
{
    if(front==rear)
        throw"下溢异常";
    else
        return front->next->data;
}

(7)判空操作
判断front是否=rear

template <typename DataType>
int LinkQueue::Empty()
{
    if(front==rear)
        return 1;       //链队列为空
    else
        return 0;
}

三、拓展提高

1.两栈共享空间

思想:利用顺序栈的单向延伸性,使用一个数组存储两个栈,一个栈位于数组的始端一个栈位于数组末端,两栈各自向中间延伸,避免了存储空间的浪费
条件:两个栈空间需求有相反的需求,一个栈增长时一个栈缩短。
在这里插入图片描述
(1)top1和top2分别为栈1和栈2的栈顶,StackSize为数组大小。
(2)栈1的底位于数组下标为0的一端,栈2的底位于下标为StackSize-1的一端。

2.两栈共享空间的实现
(1)声明
设整型变量i只有1、2两个值,取i=1表示对栈1操作,取i=2表示对栈2进行操作

const int StackSize = 100;
template <typename DataType>
class BothStack
{
public:
    BothStack();                 //两栈分别初始化
    ~BothStack();
    void Push(int i,DataType x)      //入栈操作,将元素x压入栈i
    DataType Pop(int i);             //出栈操作,对栈i执行出栈操作
    DataType GetTop(int i);          //取栈i的栈元素
    int Empty(int i);                //判断栈i是否为空

private:
    DataType data[StackSize];       //存放两个栈的数组
    int top1,top2;                 //两个栈的栈顶指针,分别为各自栈顶元素的在数组的下标
};

(2)入栈操作
数组中没有空闲单元时栈满,此时栈1栈顶和栈2栈顶元素相邻。
即top1 = top2-1或top2 = top1+1
入栈1时top1+1,入栈2时top2-1

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

(3)出栈操作
top1 = -1时栈1为空,top2 = StackSize时栈2为空

template <typename DataType>
DataType BothStack::Pop(int i)
{
    if (i==1)
    {
        if (top==-1)
            throw"下溢";
        return data[top1--];
    }
    if (i==2)
    {
        if (top2==StackSize)
            throw"下溢";
        return data[top2++];       //注意栈2删除元素,top2是+1
    }
}

3.双端队列
允许队列在两端进行插入删除操作称为双端队列
两端插入一端删除称为二进一出,两端删除一端插入称为一进二出。
在这里插入图片描述
双端队列可采用循环队列的存储方式,在队头入队时需先将新元素x插入到front处,队头front在循环意义下-1,在队尾出队时,先将rear处队尾元素暂存,再把rear在循环意义下-1。

4.双端队列的实现
没写

5.括号匹配问题
施工中。。。。

6.表达式求值问题
施工中。。。。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值