堆栈和队列

本文详细介绍了堆栈和队列两种数据结构。堆栈是只允许在表的一端进行操作的线性表,包括顺序堆栈和链接堆栈的实现,常见应用场景。队列则是允许在表一端插入,另一端删除的线性表,讨论了顺序队列、循环队列和链式队列的实现及特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆栈

栈:是只允许在表的一端操作的线性表
采用顺序存储结构的堆栈称为顺序堆栈,可以利用一个有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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值