堆栈
栈:是只允许在表的一端操作的线性表
采用顺序存储结构的堆栈称为顺序堆栈,可以利用一个有M个元素的数组STACK[0,…M-1]来表示;
定义一个整型变量top作为堆栈的栈顶指针,他指出某一时刻堆栈栈顶元素的位置,当堆栈不为空是,top为数组某一元素的下标值,当堆栈为空时,top=-1;
顺序堆栈的基本算法
一、入栈
在容量为M的堆栈中插入一个新的元素item,top指向栈顶;
首先判断栈是否已满,若不满将top向前移动一位
二、退栈
从堆栈中退出当前栈顶元素,并保存在变量item中,同时修改栈顶指针的位置
#include <string>
#include <iostream>
#define M 6 //定义堆栈的最大容量
typedef int SElemType; //为int类型起一个别名ElemType
//初始化一个堆栈
void INITIALS(int &top){
top=-1;
}
// 测试堆栈是否为空
int EMPTYS(int top){
return top==-1; //为空时返回1,否则返回0
}
// 测试堆栈是否已满
int FULLS(int top){
return top==M-1;
}
// 进栈
int PUSH(SElemType STACK[],int &top,SElemType item){
// top为栈顶指针变量
if(FULLS(top)){
printf("堆栈已满,top:%d\n",top);
return 0; //堆栈已满,插入失败,返回0
}else{
STACK[++top]=item;
printf("push item:%d,top:%d\n",item,top);
return 1; //堆栈未满,插入成功,返回1
}
}
// 删除(退栈):从堆栈中退出当前栈顶元素,并保存在变量item中,同时修改栈顶指针的位置
int POP(SElemType STACK[],int &top,SElemType item){
if(EMPTYS(top)){
return 0; //堆栈为空,删除失败
}
item=STACK[top--]; //首先返回栈顶元素到item变量;然后执行top=top-1
printf("pop item:%d,top:%d\n",item,top);
return 1;
}
int main(){
// 初始化一个栈
SElemType STACK[M];
int top; //栈顶指针变量
INITIALS(top);
//入栈
int i;
for(i=1;i<=8;i++){
PUSH(STACK,top,i);
}
int item;
// top始终指向栈顶
POP(STACK,top,item);
return 0;
}
输出
$ g++ lineStructure.cpp &&./a.out
push item:1,top:0
push item:2,top:1
push item:3,top:2
push item:4,top:3
push item:5,top:4
push item:6,top:5
堆栈已满,top:5
堆栈已满,top:5
pop item:6,top:4
链接堆栈
采用线性链表表示的堆栈,指针变量top指向当前栈顶所在链节点的存储位置,栈为空时,top为null,入栈是采用头插法
适用场景:1、多个堆栈共享空间;2、难以估计将要进栈的元素数量;
算法:入栈、出栈
#include <string>
#include <iostream>
#define M 6 //定义堆栈的最大容量
typedef int SElemType; //为int类型起一个别名ElemType
// 链接堆栈的类型定义
typedef struct node{
SElemType data;
struct node *link;
}STNode,*STLink;
//初始化一个链接堆栈
void INITIALSLINK(STLink &top){
top=NULL;
}
// 测试链接堆栈是否为空
int EMPTYSLINK(STLink top){
return(top==NULL); //为空时返回1,否则返回0
}
// 取出当前栈顶元素
int GETTOPSLINK(STLink top,SElemType &item){
// top 指向栈顶元素所在的链节点
if(EMPTYSLINK(top)){
return 0;//堆栈为空,操作失败,返回0
}else{
item=top->data; //保存栈顶元素
return 1;
}
}
// 链接堆栈的插入
int PUSHLINK(STLink &top,SElemType item){
// top为栈顶指针变量
STLink p;
if(!(p=(STLink)malloc(sizeof(STNode)))){ //申请一个新链节点
return 0; // 插入失败,返回0
}else{
p->data=item; //将item送到新节点的数据域
p->link=top; //将top送新节点的指针域
top=p; //修改栈顶指针top的指向
return 1; //插入成功,返回1
}
}
// 删除(退栈):删除一个元素相当于删除链表中的第一个节点
int POPLINK(STLink &top,SElemType item){
if(EMPTYSLINK(top)){
return 0; //堆栈为空,删除失败
}else{
STLink p;
item=top->data; //保存被删除节点的数据信息
p=top;
top=top->link;
free(p); //释放被删除节点
printf("pop item:%d\n",item);
return 1;
}
}
int main(){
STLink top; //栈顶指针变量
INITIALSLINK(top);
//入栈
int i;
for(i=1;i<=8;i++){
PUSHLINK(top,i);
}
int item;
POPLINK(top,item);
POPLINK(top,item);
return 0;
}
输出
pop item:8
pop item:7
队列
队列是一种只允许在表的一端进行插入操作,而在表的另一端进行删除操作的线性表
队列的顺序存储结构
通过数组实现,相比于顺序栈,多了两个指针,front指向实际对头元素的前一个位置,rear指向队尾元素位置
算法
#include <string>
#include <iostream>
#define M 6
typedef int QElemType; //为int类型起一个别名ElemType
QElemType QUEEN[M]; //定义队列的最大容量
int front,rear;
// 初始化一个队列
void INITIALQ(int &front,int &rear){
front=-1;
rear=-1;
}
// 测试队列是否为空
int EMPTYQ(int front,int rear){
return front==rear; //队列为空时返回1,否则为0
}
//取当前对头元素
int GETQ(QElemType QUEEN[],int front,int rear,QElemType item){
if(EMPTYQ(front,rear)){
printf("队列为空,获取元素失败\n");
return 0;
}else{
item=QUEEN[front+1];
printf("取出队头元素:%d,rear=%d\n",item,front);
return 1;
}
}
// 队列的插入
int ADDQ(QElemType QUEEN[],int &rear,QElemType item){
// 判断队列是否已满
if(rear==M-1){
printf("队列已满,rear:%d\n",rear);
return 1;
}else{
// 从队尾入队,队头指针不动
++rear;
QUEEN[rear]=item;
printf("%d 元素入队,当前队尾rear:%d,front:%d\n",item,rear,front);
return 0;
}
}
// 队列的删除
int DELETEQ(QElemType QUEEN[],int &front){
QElemType item;
// 判断队列是否为空
if(EMPTYQ(front,rear)==1){
printf("队列为空,front:%d,rear:%d\n",front,rear);
return 0;
}else{
// 从对头删除,队尾无需改变
item=QUEEN[++front];
printf("队列已删除元素%d:,当前的front:%d,rear:%d\n",item,front,rear);
return 1;
}
}
int main(){
//初始化队列
INITIALQ(front,rear);
//入队
int i;
for(i=1;i<10;i++){
ADDQ(QUEEN,rear,i);
}
// 删除元素
DELETEQ(QUEEN,front);
return 0;
}
输出
1 元素入队,当前队尾rear:0,front:-1
2 元素入队,当前队尾rear:1,front:-1
3 元素入队,当前队尾rear:2,front:-1
4 元素入队,当前队尾rear:3,front:-1
5 元素入队,当前队尾rear:4,front:-1
6 元素入队,当前队尾rear:5,front:-1
队列已满,rear:5
队列已满,rear:5
队列已满,rear:5
队列已删除元素1:,当前的front:0,rear:5
缺陷:删除元素时,队头向左移动,当队尾到达了M-1的位置后,因为有元素删除,所以对头前面有空,但是元素却无法插入,因为对头只可以左移,这种情况下就可用循环队列来解决这种假溢出的问题
循环队列
入队时队尾右移一位,出队时队头也是移动一位
入队时如果rear是M-1,则如果rear!=front,就可以从头开始入队,入队的位置是: (rear+1)%M,
比如M=10,下标从0开始,rear=9,下一元素入队的位置(9+1)%10=0,如果front!=0则说明下表为0的位置为空,则可以入队;
同理出队时,如果队不为空,则出队的元素位置为front+1%M
// 队列的插入
int ADDQ(QElemType QUEEN[],int &rear,QElemType item){
// 判断队列是否已满
if((rear==M-1&&front==-1)||(++rear)%M==front){ //当rear已经走到尾,但是还未有元素删除时即front=-1时队也时满的
printf("队列已满,rear:%d,front:%d\n",rear,front);
return 1;
}else{
// 从队尾入队,队头指针不动
QUEEN[(rear)%M]=item;
printf("%d 元素入队,当前队尾rear:%d,front:%d\n",item,rear,front);
return 0;
}
}
// 队列的删除
int DELETEQ(QElemType QUEEN[],int &front){
QElemType item;
// 判断队列是否为空
if(EMPTYQ(front,rear)==1){ //只有rear+1从后面赶上front为队满的情况外,其余情况下的front等于rear都是队空的情况
printf("队列为空,front:%d,rear:%d\n",front,rear);
return 0;
}else{
// 从对头删除,队尾无需改变
front=(++front%M);
item=QUEEN[front];
printf("队列已删除元素%d:,当前的front:%d,rear:%d\n",item,front,rear);
return 1;
}
}
int main(){
//初始化队列
INITIALQ(front,rear);
//入队
int i;
for(i=1;i<5;i++){
ADDQ(QUEEN,rear,i);
}
// 删除元素
for(i=1;i<5;i++){
DELETEQ(QUEEN,front);
}
return 0;
}
输出
1 元素入队,当前队尾rear:0,front:-1
2 元素入队,当前队尾rear:1,front:-1
3 元素入队,当前队尾rear:2,front:-1
队列已满,rear:2,front:-1
队列已删除元素1:,当前的front:0,rear:2
队列已删除元素2:,当前的front:1,rear:2
队列已删除元素3:,当前的front:2,rear:2
队列为空,front:2,rear:2
队列的链式存储结构
队列采用的是链式,存储结构,因为数据的元素频繁的插入和删除
队列中的每一个元素对应链表中的一个链结点,第一个链结点指针定义为对头指针front,定义rear为队尾指针指向队列的最后一个链结点;
并且限定只能对头删除,队尾插入。
链接队列为空的条件是front为NULL。插入元素就是在链表的表尾结点后添加一个新链结点;删除元素就是删除链表的第一个结点。
链表队列算法
#include <string>
#include <iostream>
#define M 3
typedef int QElemType;
typedef struct node{
QElemType data;
struct node *link;
}QNode,*QLink; //定义一个链表队列类型
// 初始化一个队列
void INITIALQLINK(QLink &front,QLink &rear){
front=rear=NULL;
}
// 测试队列是否为空
int EMPTYQLINK(QLink front){
return front==NULL; //队列为空时返回1,否则为0
}
//取当前对头元素
int GETQLINK(QLink front,QElemType &item){
if(EMPTYQLINK(front)){
printf("队列为空,获取元素失败\n");
return 0;
}else{
item=front->data;
printf("取出队头元素:%d\n",item);
return 1;
}
}
// 队列的插入
int ADDQLINK(QLink &front,QLink &rear,QElemType item){
QLink p;
if(!(p=(QLink)malloc(sizeof(QNode)))){ //申请一个链结点
return 0; //插入失败,返回0
}else{
p->data=item; //将item送新结点的数据域
p->link=NULL; //将新结点的指针域置空
}
if(front==NULL){ // 判断队列是否为空,如果为空需要修改front的指向
front=p;
}else{
rear->link=p; //将item插入非空队的情况
}
rear=p; //修改队尾指针rear的指向
printf("插入数据:%d\n",item);
return 1; //插入成功,返回1
}
// 队列的删除
int DELETEQLINK(QLink &front){
QLink p;
// 判断队列是否为空
if(EMPTYQLINK(front)==1){
printf("队列为空\n");
return 0;
}else{
// 从对头删除,队尾无需改变
p=front;
front=front->link;
free(p); //当删除最后一个元素时,队尾也丢失了
printf("队列已删除元素%d:\n",p->data);
return 1;
}
}
// 队列的销毁
// 和删除节点逻辑一样,只是为了节省内存,将rear充当临时变量的角色
void DESLINKQ(QLink &front,QLink &rear){
while (front)
{
rear=front->link;
free(front);
front=rear;
}
}
int main(){
//初始化队列
QLink front,rear;
INITIALQLINK(front,rear);
//入队
int i;
for(i=1;i<5;i++){
ADDQLINK(front,rear,i);
}
// 删除元素
for(i=1;i<5;i++){
DELETEQLINK(front);
}
//销毁队列
DESLINKQ(front,rear);
return 0;
}