文章目录
栈和队列是两种线性结构,他们是操作受限的线性表
栈
栈的定义
栈是限定仅在表尾进行插入或删除的线性表
表尾称为栈顶, 表头端成为栈底。
不含任何元素的空表成为空栈。
常用的操作系统中,栈是由高地址向低地址生长的,即栈顶地址低,栈底地址高
对栈的操作
栈的操作只有两种
1:将数据元素存储起来,叫做入栈 || 压栈
2:将数据元素从栈中提出,叫做出栈 || 弹栈
栈的先进后出原则
假设栈Stack=(a1,a2,…,an),则称a1为栈底元素,an为栈顶元素。
栈中元素按照a1->an的顺序进栈,则退栈的第一个元素应为栈顶元素,即an
这就是栈的先进后出(LIFO: last in first out)原则。
栈的表示与实现
顺序栈
即栈的顺序存储结构是利用一组地址连续的存储单元依次存放到自栈底到栈顶的数据元素。
顺序栈的实现利用了数组的形式
top指针为栈顶指针,指向栈顶元素的下一个位置,压栈top++,弹栈top–
base指针为栈底指针,始终指向栈底的位置。
top和base初始时都指向栈底
top = = base为栈空条件,base==NULL则栈不存在
压栈时要先判断栈是否满
不满然后进行赋值,再进行top指针移动。
数据结构
class Sqstack
{
public:
int* base;
int* top;
int length;
public:
Sqstack();
~Sqstack();
int push(int n);
int pop();
int gettop();
int showelem();
};
共享栈
在程序中定义多个栈,多个栈共享存储空间。
作用:可在一定程度上避免空间浪费
实现
const int Max=100;
int Stack[Max];
top1=0;
top2=Max-1;
压栈算法
void push1(int x)
{
if(top1>top2)
OVERFLOW;
else
{
Stack[top1++]=x;
}
}
void push2(int y)
{
if(top1>top2)
OVERFLOW;
else
{
Stack[top2--]=y;
}
}
出栈算法
int pop1()
{
if(top1==0)
{
UNDERFLOW;
return NULL;
}
top1--;
return Stack[top1];
}
int pop2()
{
if(top2==0)
{
UNDERFLOW;
return NULL;
}
top2++;
return Stack[top2];
}
链栈
以链式结构表示栈
优点:链栈无满栈问题,且空间可扩充
其栈顶在链头处(在这里进行插入、删除)
链栈适用于多栈操作
栈的应用
数制转换
括号匹配检测
行编辑程序
迷宫问题
表达式求值
递归
队列
队列的定义
队列是只允许在表的一端进行插入,而在另一端删除的线性表。
允许插入的一端叫做表尾(rear),允许删除的一端叫做表头(front)。
队列的先进先出性质
假设队列Queue=(a1,a2,…,an),则称a1为队头元素,an为队尾元素。
队列中元素按照a1->an的顺序进入,则退出的顺序也应该是a1->an
这就是队列的先进先出(FIFO: first in first out)原则。
队列的表示与实现
链式队列
需要两个分别指向队头和队尾的指针 front rear.
链队列无队满问题,但有队空问题
如何判断空队列
若front和rear都指向头结点,或者==NULL,则为空队列。
队列插入元素时,rear后移
队列删除元素时,front->next后移
具体代码
#include <iostream>
using namespace std;
class Lnode
{
public:
int data;
Lnode* next;
};
class Lqueue
{
public:
Lnode* frontt;
Lnode* rear;
public:
Lqueue();
~Lqueue();
int enqueue(int n);
int dequeue();
int gettop();
int showqueue();
};
Lqueue::Lqueue()
{
frontt=new Lnode;
rear=new Lnode;
frontt=rear;
frontt->next=NULL;
}
Lqueue::~Lqueue()
{
Lnode* p=new Lnode;
p=frontt;
while(p!=rear)
{
Lnode* e=p;
p=p->next;
delete(e);
}
}
Lqueue::enqueue(int n)
{
Lnode* p=new Lnode;
p->data=n;
p->next=NULL;
rear->next=p;
rear=p;
return 1;
}
Lqueue::dequeue()
{
if(frontt==rear)
{
cout<<"Queue is empty"<<endl;
return -1;
}
Lnode* p=new Lnode;
p=frontt->next;
cout<<p->data<<" is deleted"<<endl;
frontt->next=p->next;
delete(p);
return 1;
}
Lqueue::gettop()
{
if(frontt==rear)
{
cout<<"Queue is empty"<<endl;
return -1;
}
Lnode* p=frontt;
cout<<p->next->data<<" is top value"<<endl;
return 1;
}
Lqueue::showqueue()
{
if(frontt==rear)
{
cout<<"Queue is empty"<<endl;
return -1;
}
Lnode* p=frontt;
cout<<"Queue is below : ";
while(p!=rear)
{
p=p->next;
cout<<p->data<<" ";
}
cout<<endl;
return 1;
}
int main()
{
Lqueue myqueue;
int len,enval,env;
cout<<"Please input length of queue"<<endl;
cin>>len;
for(int i=0;i<len;i++)
{
cout<<"Please input "<<i+1<<"th value"<<endl;
cin>>enval;
myqueue.enqueue(enval);
}
myqueue.showqueue();
cout<<endl;
cout<<"Now enqueue"<<endl;
cout<<"Please input value to en"<<endl;
cin>>env;
myqueue.enqueue(env);
myqueue.showqueue();
cout<<"Now dequeue"<<endl;
myqueue.dequeue();
myqueue.showqueue();
cout<<endl;
cout<<"Now get top value"<<endl;
myqueue.gettop();
myqueue.showqueue();
}
顺序队列
顺序队列主要使用循环队列的形式表示
为什么不使用普通的顺序队列呢?
首先看顺序队列的定义
即用一组地址连续的存储单元依次存放从队列头到队列尾的元素,front和rear指针分别指向队头和队尾的位置。
对于入队操作,rear=rear+1
对于出队操作,front=front+1
因此,头指针永远指向第一个元素,而尾指针永远指向队尾元素的下一个位置
因此可以看到,顺序队列会有溢出问题。
而且更糟糕的是,有时候这种溢出会是假溢出
为了解决假溢出问题,我们提出了循环队列的思想
在这里我们对入队/出队操作中指针的变化有了新的实现方式
初始化
front=rear=0;
入队
front=(front+1)%Max;
出队
rear=(rear+1)%Max;
队空条件
front==rear;
队满条件
front==(rear+1)%Max;
可以分析一波啊
这波我们的变化的核心是队空条件变成了front==rear
而front 和 rear指针的变化改变都是为了这个条件服务的。
另外提醒一波队满的样子
注意这个队满并不是真满,而是空了一个元素出来。
记住哟!