王道——数据结构——栈和队列(1)

系列文章目录

其他章节相关文章

王道——数据结构——树与二叉树(1)

本章节其他相关文章



前言

本文为王道数据结构的第三章——栈和队列的编程题。
运行软件:vscode
使用c++文件编写


一、栈的顺序存储

一、顺序栈

1、头文件并使用宏定义给出存储最大结点数

#include<stdio.h>
#include<stdlib.h>

#define MaxSize 8

2、栈的结构体定义

typedef struct{
    int data[MaxSize];  // 静态数组存放栈中元素
    int top;  // 栈顶指针,指向栈顶的数组下标
}SqStack;

3、初始化

//初始化栈, 指向栈顶元素
void InitStack(SqStack &S){
    S.top = -1;
}

4、新元素入栈

bool Push(SqStack &S, int x){
    if(S.top == MaxSize-1)  // 栈满、报错
        return false;
    S.top += 1;  // 指针先+1
    S.data[S.top] = x;  // 新元素入栈
    // S.data[++S.top] = x;  //top先自加1然后找位置
    return true;
}

5、栈顶元素出栈

bool Pop(SqStack &S, int &x){
    if(S.top == -1)
        return false;
    x = S.data[S.top--];  // 先使用top,然后top自减1
    return true;
}

6、读取栈顶元素(不将栈顶元素弹出)

bool GetTop(SqStack S, int &x){
    if(S.top == -1)
        return false;
    x = S.data[S.top];
    return true;
}

7、判断栈是否为空

//判空
bool SqStackEmpty(SqStack S){
    if(S.top == -1)
        return true;
    return false;
}

8、建立并测试栈

void testStack(){
    SqStack S; // 声明一个顺序栈(分配空间)
    InitStack(S);
    int x;
    scanf("%d", &x);
    while(x!=9999){
        if(Push(S, x)){
            printf("已成功入栈请继续输入:");
            scanf("%d", &x);
        }
        else{
            printf("栈已满\n");
            break;
        }
    }
    GetTop(S, x);
    printf("%d\n",x);
    while(S.top != -1){
        Pop(S, x);
        printf("%d ", x);
    }
}

二、共享栈

共享栈结构为:两个栈共用一片数组空间,一个栈自底向上,另一个栈自顶向下。

1、共享栈的结构体定义

typedef struct{  // 栈顶指针指向栈顶元素
    int data[MaxSize];
    int top0;
    int top1;
}ShStrack;

2、初始化

//初始化栈, 指向栈顶元素
void InitShStrack(ShStrack S){
    S.top0 = -1;  // 第一个栈自底向上
    S.top1 = MaxSize;  // 第二个栈自顶向下
}

3、向第一个栈添加元素

// 向第一个栈添加元素
bool PushShStrack_0(ShStrack &S, int x){
    if(S.top0+1 == S.top1)  //判断栈是否已满
        return false;
    S.data[++S.top0] = x;
    return true;
}

4、向第一个栈删除并返回栈顶元素

// 向第一个栈删除并返回栈顶元素
bool PopShStrack_0(ShStrack &S, int &x){
    if(S.top0 == -1)
        return false;
    x = S.data[S.top0--];
    return true;
}

5、向第一个栈取出栈顶元素

// 向第一个栈取出栈顶元素
bool GetTop_0(ShStrack S, int &x){
    if(S.top0 == -1)
        return false;
    x = S.data[S.top0];
    return true;
}

6、向第二个栈添加元素

// 向第二个栈添加元素
bool PushShStrack_1(ShStrack &S, int x){
    if(S.top1-1 == S.top0)
        return false;
    S.data[--S.top1] = x;
    return true;
}

7、向第二个栈删除并返回栈顶元素

// 向第二个栈删除并返回栈顶元素
bool PopShStrack_1(ShStrack &S, int &x){
    if(S.top1 == MaxSize)
        return false;
    x = S.data[S.top1++];
    return true;
}

8、取出第二个栈的栈顶元素

// 取出第二个栈的栈顶元素
bool GetShStrack_1(ShStrack S, int &x){
    if(S.top1 == MaxSize)
        return true;
    x = S.data[S.top1];
    return true;
}

二、栈的链式存储

一、链栈(不带头结点)

1、头文件

#include<stdio.h>
#include<stdlib.h>

2、结点的结构体定义

//定义一个结点
typedef struct LinkNode{
    int data;
    struct LinkNode *next;
}*LiStack;

3、初始化

//初始化(不带头结点)
void InitLiStack(LiStack &L){
    L = NULL;
}

4、进栈

//进栈(不带头结点)
bool Push(LiStack &L, int x){
    LiStack p = (LiStack)malloc(sizeof(LiStack));
    if(p == NULL)
        return false;
    p->data = x;
    p->next = L;
    L = p;
    return true;
}

5、出栈

// 出栈(不带头结点)
bool Pop(LiStack &L, int &x){
    if(L == NULL)
        return false;
    x = L->data;
    LiStack p;
    p = L;
    L = L->next;
    free(p);
    return true;
}

6、取栈顶元素

// 取栈顶元素
bool GetTop(LiStack L, int &x){
    if(L==NULL)
        return false;
    x = L->data;
    return true;
}

7、建立栈并测试运行

void test(){
    LiStack L;
    InitLiStack(L);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        if(Push(L, x)){
            printf("入栈成功!请继续输入(9999为退出):");
            scanf("%d", &x);
        }
        else{
            printf("栈满!\n");
        }
    }
    GetTop(L, x);
    printf("%d\n", x);
    while(L!=NULL){
        Pop(L, x);
        printf("%d ", x);
    }
}

二、链栈(带头结点)

1、结点的结构体定义
与不带头结点相同

2、初始化

//初始化(带头结点)
void InitLiStackH(LiStack &L){
    L = (LiStack)malloc(sizeof(LiStack));
    L->next = NULL;
}

3、进栈

//进栈(带头结点)
bool PushH(LiStack &L, int x){
    LiStack p = (LiStack)malloc(sizeof(LiStack));
    if(p == NULL)
        return false;
    p->data = x;
    p->next = L->next;
    L->next = p;
    return true;
}

4、出栈

// 出栈(带头结点)
bool PopH(LiStack &L, int &x){
    if(L->next == NULL)
        return false;
    LiStack p=L->next;
    x = p->data;
    L->next = p->next;
    free(p);
    return true;
}

5、取栈顶元素

//取出栈顶元素(带头结点)
bool GetTopH(LiStack L, int &x){
    if(L->next == NULL)
        return false;
    x = L->next->data;
    return true;
}

6、建立栈并测试运行

void testH(){
    LiStack L;
    InitLiStackH(L);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        if(PushH(L, x)){
            printf("入栈成功!请继续输入(9999为退出):");
            scanf("%d", &x);
        }
        else{
            printf("内存无空间\n");
        }
    }
    GetTopH(L, x);
    printf("%d\n", x);
    while(L->next!=NULL){
        PopH(L, x);
        printf("%d ", x);
    }
}

三、队列的顺序存储

循环队列,将队尾的表达式写成Q.rear = (Q.rear+1)%MaxSize 当Q.rear == MaxSize时,队尾指针会重新指向位序为0的数据,形成循环。
判空条件为 Q.rear == Q.front ,所以判满的时候要牺牲一个空间存放Q.rear指针,不能使二者指向同一个位置所以判满条件为(Q.rear+1)%MaxSize == Q.front
★★★ 队列元素个数 = (Q.rear + MaxSize - Q.front) % MaxSize★★★
本代码基于队尾指针指向队尾的下一个元素,如指向队尾元素则代码稍有不同

一、使用一个存储空间存放尾指针

1、头文件并使用宏定义给出存储最大结点数

#include<stdio.h>
#include<stdlib.h>

#define MaxSize 20

2、队列的结构体定义

//定义一个队列
typedef struct{
    int data[MaxSize];
    int front, rear;  // front指向队头,rear指向队尾的下一个元素
    //int size;  //方法一:不想浪费一个空间存放Q.rear指针,需要设置一个变量size初始为0,记录队列长度
    // 插入成功size++,删除成功size--
    //判空 Q.size == 0; 判满 Q.size == MaxSize;
    //int tag //方法二:不想浪费一个空间存放Q.rear指针,需要设置一个变量tag,记录最近一个操作是插入还是删除,插入为1,删除为0
    //只有插入才会导致队满,只有删除才会导致队空,插入成功:tag = 1; 删除成功:tag = 0
    //判空 Q.rear == Q.front && Q.tag == 0; 判满:Q.rear == Q.front && Q.tag == 1;
}SqQueue;

3、初始化

//初始化一个队列 
void InitSqQueue(SqQueue &Q){
    Q.front = Q.rear = 0;
}

4、新元素入队

// 元素入队
bool EnSqQueue(SqQueue &Q, int x){
    if((Q.rear+1)%MaxSize == Q.front) // 特别注意判满条件
        return false;
    Q.data[Q.rear] = x;
    Q.rear = (Q.rear +1)%MaxSize;
    return true;
}

5、队尾元素出队

// 元素出队
bool DeSqQueue(SqQueue &Q, int &x){
    if(Q.rear == Q.front)
        return false;
    x = Q.data[Q.front];
    Q.front = (Q.front+1)%MaxSize;
    return true;
}

6、读取队首元素(不将队首元素弹出)

bool GetHead(SqQueue Q, int &x){
    if(Q.rear == Q.front)
        return false;
    x = Q.data[Q.front];
    return true;
}

7、判断队是否为空

//判空
bool SqQueueEmpty(SqQueue Q){
    if (Q.rear == Q.front)
        return true;
    return false;
}

8、建立并测试队列

void test(){
    SqQueue Q;
    InitSqQueue(Q);
    int x;
    scanf("%d", &x);
    while(x != 9999 ){
        if(EnSqQueue(Q, x)){
            printf("已入队!请继续输入(9999为退出):");
            scanf("%d", &x);
        }
        else{
            printf("队满\n");
            break;
        }
    }
    GetHead(Q, x);
    printf("%d\n", x);
    while(Q.rear!= Q.front){
        DeSqQueue(Q, x);
        printf("%d ", x);
    }
}

二、队尾指针指向队尾元素,并设置size判空判满

1、结构体定义

typedef struct{
    int data[MaxSize];
    int rear, front, size;
}SqQueueD;

2、初始化

//初始化
void InitSqQueueD(SqQueueD &Q){
    Q.front = 0;
    Q.size = 0;
    Q.rear = MaxSize-1;
}

3、判空

//判空
bool SqQueueDEmpty(SqQueueD Q){
    if(Q.size == 0)
        return true;
    return false;
}

4、元素入队

//元素入队
bool InSqQueueD(SqQueueD &Q, int x){
    if(Q.size == MaxSize)
        return false;
    Q.rear = (Q.rear+1)%MaxSize;
    Q.data[Q.rear] = x;
    Q.size++;
    return true;
}

5、元素出队

// 元素出队
bool DeSqQueueD(SqQueueD &Q, int &x){
    if(Q.size == 0)
        return false;
    x = Q.data[Q.front];
    Q.front = (Q.front+1)%MaxSize;
    Q.size--;
    return true;
}

6、取队头元素

// 取队头元素
bool GetHeadD(SqQueueD Q, int &x){
    if(Q.size == 0)
        return false;
    x = Q.data[Q.front];
    return true;
}

7、建立并测试队列

void testD(){
    SqQueueD Q;
    InitSqQueueD(Q);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        if(InSqQueueD(Q, x)){
            printf("已入队!请继续输入(9999为退出):");
            scanf("%d", &x);
        }
        else{
            printf("队满\n");
            break;
        }
    }
    GetHeadD(Q, x);
    printf("%d\n", x);
    while(Q.size != 0){
        DeSqQueueD(Q, x);
        printf("%d ", x);
    }
}

三、队尾指针指向队尾元素,并设置tag判空判满 ,

思路:只有删除才会使队列为空,所以删除时将tag置为0,只有添加元素才会使队满,所以入队时将tag置为1。

1、结构体定义

typedef struct SqQueueT{
    int data[MaxSize];
    int front, rear, tag;
}SqQueueT;

2、初始化

//初始化
void InitSqQueueT(SqQueueT &Q){
    Q.front = 0;
    Q.rear = 0;
    Q.tag = 0;
}

3、判空

//判空
bool QueueTEmpty(SqQueueT Q){
    if(Q.front == Q.rear && Q.tag == 0)
        return true;
    return false;
}

4、元素入队

//入队
bool EnQueueT(SqQueueT &Q, int x){
    if(Q.rear == Q.front && Q.tag == 1)
        return false;
    Q.data[Q.rear] = x;
    Q.rear = (Q.rear+1)%MaxSize;
    Q.tag = 1;
    return true;
}

5、元素出队

//出队
bool DeQueueT(SqQueueT &Q, int &x){
    if(Q.rear == Q.front && Q.tag == 0)
        return false;
    x = Q.data[Q.front];
    Q.front = (Q.front + 1)%MaxSize;
    Q.tag = 0;
    return true;
}

6、取队头元素

//取出队头元素
bool GetHeadT(SqQueueT Q, int &x){
    if(Q.rear == Q.front && Q.tag == 0)
        return false;
    x = Q.data[Q.front];
    return true;
}

7、建立并测试队列

void testT(){
    SqQueueT Q;
    InitSqQueueT(Q);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        if(EnQueueT(Q, x)){
            printf("已入队!请继续输入(9999为退出):");
            scanf("%d", &x);
        }
        else{
            printf("队满\n");
            break;
        }
    }
    GetHeadT(Q, x);
    printf("%d\n", x);
    while(!QueueTEmpty(Q)){
        DeQueueT(Q, x);    
        printf("%d ", x);
    }
}

四、队列的链式存储

多了一个结构体存放首尾指针

一、链队(带头结点)

1、头文件

#include<stdio.h>
#include<stdlib.h>

2、队列的结构体定义

//定义链式队列的结点
typedef struct LinkNode{
    int data;
    struct LinkNode *next;
}LinkNode;

//定义链式队列的队头和队尾指针
typedef struct{
    LinkNode *front, *rear;
}LinkQueue;

3、初始化

//初始化(带头结点)
void InitLinkQueueD(LinkQueue &Q){
    Q.front = Q.rear = (LinkNode *)malloc(sizeof(LinkNode));
    Q.front->next =NULL;
}

4、新元素入队

//新元素入队
bool EnQueueD(LinkQueue &Q, int x){
    LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
    if(p == NULL)
        return false;
    p->data = x;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
    return true;
}

5、队尾元素出队

//元素出队
bool DeQueueD(LinkQueue &Q, int &x){
    if(Q.rear == Q.front)
        return false;
    LinkNode *p = Q.front->next;
    x = p->data;
    Q.front->next = p->next;
    if(p == Q.rear)  //修改队尾指针
        Q.rear = Q.front;
    free(p);
    return true;
}

6、建立并测试队列

//测试(带头结点)
void testD(){
    LinkQueue Q;
    InitLinkQueueD(Q);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        EnQueueD(Q, x);
        printf("入队成功!请继续输入(9999为退出):");
        scanf("%d", &x);
    }
    while(Q.rear != Q.front){
        DeQueueD(Q, x);
        printf("%d ", x);
    }
}

一、链队(不带头结点)

1、队列的结构体定义
与带头结点一样

3、初始化

//初始化(不带头结点)
void InitLinkQueue(LinkQueue &Q){
    Q.front = Q.rear = NULL;
}

4、新元素入队

//新元素入队
bool EnQueue(LinkQueue &Q, int x){
    LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
    p->data = x;
    p->next = NULL;
    if(Q.front == NULL){
        Q.front = Q.rear = p;
        return true;
    }
    Q.rear->next = p;
    Q.rear = p;
    return true;
}

5、队尾元素出队

//元素出队
bool DeQueue(LinkQueue &Q, int &x){
    if(Q.front == NULL)
        return false;
    LinkNode *p = Q.front;
    x = p->data;
    Q.front = p->next;
    if(p == Q.rear)  //修改队尾指针
        Q.rear = Q.front;
    free(p);
    return true;
}

6、建立并测试队列

//测试(不带头结点)
void test(){
    LinkQueue Q;
    InitLinkQueue(Q);
    int x;
    scanf("%d", &x);
    while(x != 9999){
        EnQueue(Q, x);
        printf("入队成功!请继续输入(9999为退出):");
        scanf("%d", &x);
    }
    while(Q.rear != Q.front){
        DeQueue(Q, x);
        printf("%d ", x);
    }
}

五、3.3节

1、假设一个算术表达式中包含圆括号、方括号、花括号3种类型括号,编写一个算法来判别表达式中的括号是否匹配,以字符“\0”作为算术表达式的结束符。

//第一题 遇见'(','{','['入栈,否则弹出栈顶元素,是否匹配,匹配则继续进行,否则返回false。
bool pipei(char str[]){
    LinkStack S;
    InitLinkStack(S);
    char e;
    int i = 0;
    while(str[i] != '\0'){
        if(str[i] == '(' || str[i] == '[' || str[i] == '{')
            Push(S, str[i]);
        else{
            if(!Pop(S, e))
                return false;
            if(str[i] == ')' && e != '(')
                return false;
            if(str[i] == '}' && e != '{')
                return false;
            if(str[i] == ']' && e != '[')
                return false;
            }
        i++;
    }
    if(S->lenght == 0)
        return true;
    return false;
}

2、按下图所示铁道进行车厢调度(注意,两侧铁道均匀为单向行驶道,火车调度站有一个用于调度的“栈道”),火车调度站的入口处有n节硬座和软座车厢(分别用H和S表示)等待调度,试编写算法,输出对者n节车厢进行调度的操作(即入栈或出栈操作)序列,以使所有的软座车厢都被调整到硬座车厢之前。

//第二题 输入一个只含H、S的字符串,H代表硬车厢,S代表软车厢,依次扫描字符串,把H压入栈中,S跳过,扫描完字符穿依次弹出栈中所有元素
void test2(char str[]){
    LinkStack S;
    InitLinkStack(S);
    int i = 0;
    char e;
    while(str[i] != '\0'){
        if(str[i] == 'S')
            printf("%c", str[i]);
        else
            Push(S, str[i]);
        i++;
    }
    while(S->lenght != 0){
        Pop(S, e);
        printf("%c", e);
    }
}

3、利用一个栈实现以下递归函数的非递归运算

//第三题 建立栈将每一层的递归主题压入栈中,使用循环,其终止条件为n= 1||n=0
void test3(int n,double x){
    typedef struct{
        int no;
        double value;
    }st[MaxSize];
    st S;
    int top = -1;
    double fv1, fv2;
    for(int i = n; i>=2;i--){
        top++;
        S[top].no = n;
        n--;
    }
    S[top+1].value = 2*x;
    S[top+2].value = 1;
    while(top != -1){
        fv1 = S[top+1].value;
        fv2 = S[top+2].value;
        S[top].value = fv1*2*x-2*(S[top].no-1)*fv2;
        top--;
    }
    printf("%.f", S[top+1].value);
}

4、某汽车轮渡口,过江渡船每次能载 10 辆车过江。过江车辆分别为客车类和货车类,上船有如下规定:同类车先到先上船,客车先于货车上渡船,且每上 4 辆客车,才允许上一辆货车;若等待客车不足 4 辆则以货车代替;若无货车等待则允许客 车都上船。设计一个算法模拟渡口管理。


// 3.3 
// 第四题 
void test4(){
    SqQueue Q;  // 过江渡船载渡队列
    SqQueue q1;  // 客车队列
    SqQueue q2;  // 货车队列
    InitSqQueue(Q);  
    InitSqQueue(q1);
    InitSqQueue(q2);
    int i = 0, j = 0;
    while(j<0){  // j表示渡船上的总车辆数
        if(!SqQueueEmpty(q1) && i <4){  // 不足十辆时
            DeSqQueue(q1, x);  // 客车队列不空,则未上足四辆
            EnSqQueue(Q, x);  // 客车上船
            i++;
            j++;
        }
        else if(i == 4 && !SqQueueEmpty(q2)){  // 客车上足四辆
            DeSqQueue(q2, x);  // 货车上船
            EnSqQueue(Q, x);
            j++;
            i = 0  // 重新计数
        } 
        else{  // 有一个队列为空
            while(j<10 && i< 4 && !SqQueueEmpty(q2)){  // 客车队列空
                DeSqQueue(q2, x);  // 货车上船
                EnSqQueue(Q, x);
                i++;
                j++;
            }
            i = 0;
        }
        if(SqQueueEmpty(q1) && SqQueueEmpty(q2))  
            j = 11;  // 客车货车加起来不足十辆
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值