Week1Day4A:队列和栈的应用【2023 安全创客实践训练|笔记】

文章详细介绍了武汉大学国家网络安全学院2022级学生在第三学期的996实训课程中涉及的数据结构,包括队列、栈、循环队列和字符串栈的实现,并展示了使用这些数据结构解决编程竞赛中的问题,如报数游戏和括号匹配等。
摘要由CSDN通过智能技术生成

内容为武汉大学国家网络安全学院2022级大一第三学期“996”实训课程中所做的笔记,仅供个人复习使用,如有侵权请联系本人,将与15个工作日内将博客设置为仅粉丝可见。
 


队列的演示


栈的演示


队列和栈

队列:

int q[1000], l = 0, r = 0;  // 初始化
q[r++] = x;  // 入队
int now = q[l];  // 获取队首
l++;  // 出队
q[r++] = y;
printf("%d\n", q[l++]); // 获取队首同时出队
if (l < r) {  // 判断是否为空
    printf("not empty\n");
}

栈:

int s[100], top = -1;
s[++top] = x;  // 入栈
int now = s[top]; // 获取栈顶元素
top--; // 出栈
s[++top] = y;
printf("%d\n", s[top--]);  // 获取栈顶同时出栈
if (top >= 0) {  // 栈不为空
    printf("not empty\n");
}

循环队列:

const int size = 100000;
int q[size], l = 0, r = 0;  // 初始化
q[r] = x; r = (r + 1) % size;  // 入队
int now = q[l];  // 获取队首
l = (l + 1) % size;  // 出队
q[r] = y; r = (r + 1) % size;
printf("%d\n", q[l]); l = (l + 1) % size; // 获取队首同时出队
if (l != r) {  // 判断是否为空
    printf("not empty\n");
}

字符串栈:

char s[100][1000], top = -1;
scanf("%s", s[++top]);  // 入栈
char t[1000];
strcpy(t, s[top]); // 获取栈顶元素
top--; // 出栈
strcpy(s[++top], t);
printf("%s\n", s[top--]);  // 获取栈顶同时出栈
if (top >= 0) {  // 栈不为空
    printf("not empty\n");
}

报数

  • 时间限制:1000ms
  • 空间限制:131072K
  • 语言限制:C语言

有 n 个小朋友做游戏,他们的编号分别是 1,2,3...n。他们按照编号从小到大依次顺时针围成一个圆圈,从第一个小朋友开始从 1 报数,依次按照顺时针方向报数(加一),报 m 的人会离开队伍,然后下一个小朋友会继续从 1 开始报数,直到只剩一个小朋友为止。

输入格式

第一行输入两个整数,n,m。(1≤n,m≤1000)

输出格式

输出最后一个小朋友的编号,占一行。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
10 5
样例输出
3

题解:用队列模拟。

我的答案

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

#define MAXN 1000

typedef struct {
    int data[MAXN];
    int front, rear;
} Queue;

void initQueue(Queue *q) {
    q->front = q->rear = 0;
}

int isQueueEmpty(Queue *q) {
    return q->front == q->rear;
}

void enQueue(Queue *q, int x) {
    q->data[q->rear] = x;
    q->rear = (q->rear + 1) % MAXN;
}

int deQueue(Queue *q) {
    int x = q->data[q->front];
    q->front = (q->front + 1) % MAXN;
    return x;
}

int main() {
    int n, m;
    if (scanf("%d%d", &n, &m) != 2) {
        fprintf(stderr, "输入错误\n");
        exit(1);
    }
    Queue q;
    initQueue(&q);
    for (int i = 1; i <= n; i++) {
        enQueue(&q, i);
    }
    int cnt = 0;
    while (!isQueueEmpty(&q)) {
        int x = deQueue(&q);
        cnt++;
        if (cnt == m) {
            cnt = 0;
        } else {
            enQueue(&q, x);
        }
    }
    printf("%d\n", q.data[(q.rear - 1 + MAXN) % MAXN]);
    return 0;
}

敲7

  • 时间限制:1000ms
  • 空间限制:131072K
  • 语言限制:C语言

有一种酒桌游戏叫做“敲7”,规则是从一个人开始,说出任意数字,其他人会顺序往后报,如果一个数字包含 7,或者是 7 的倍数,那么需要敲打杯子或盘子,不能说出。

现在 n 个人围坐在一个圆桌周围,他们编号从 1 到 n 顺时针排列。从某一人开始报出一个数字,其他人会按照顺时针方向顺序往后报(加一),如果某个人的数字包含 7,或者是 7 的倍数,那么他将退出游戏,下一个人继续接着报,直到剩一个人为止。

输入格式

第一行输入三个整数,n,m,t。n 代表总人数,m 代表从第 m 个人开始报数,他报出的数字是 t。(1≤m≤n≤1000,1≤t≤100)

接下来的 n 行,每一行输入一个字符串,代表这 n 个人的名字,字符串的长度不超过 20。

输出格式

输出剩下的那个人的名字,占一行。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
5 3 20
donglali
nanlali
xilali
beilali
chuanpu
样例输出
chuanpu
题解:队列里储存每个人的名字,先把前 m−1 个人从队首取出放入队尾,然后模拟报数直到队列中只剩下一个人。

我的答案

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

#define MAXN 1000

typedef struct {
    int data[MAXN];
    int front, rear;
} Queue;

void initQueue(Queue *q) {
    q->front = q->rear = 0;
}

int isQueueEmpty(Queue *q) {
    return q->front == q->rear;
}

void enQueue(Queue *q, int x) {
    q->data[q->rear] = x;
    q->rear = (q->rear + 1) % MAXN;
}

int deQueue(Queue *q) {
    int x = q->data[q->front];
    q->front = (q->front + 1) % MAXN;
    return x;
}

int check(int x) {
    if (x % 7 == 0) return 1;
    while (x) {
        if (x % 10 == 7) return 1;
        x /= 10;
    }
    return 0;
}

char name[MAXN][25];

int main() {
    int n, m, t;
    scanf("%d%d%d", &n, &m, &t);
    for (int i = 1; i <= n; i++) {
        scanf("%s", name[i]);
    }
    Queue q;
    initQueue(&q);
    for (int i = m; i <= n; i++) {
        enQueue(&q, i);
    }
    for (int i = 1; i < m; i++) {
        enQueue(&q, i);
    }
    while (!isQueueEmpty(&q)) {
        int x = deQueue(&q);
        if (check(t)) {

        } else {
            enQueue(&q, x);
        }
        t++;
    }
    printf("%s\n", name[q.data[(q.rear - 1 + MAXN) % MAXN]]);
    return 0;
}

括号匹配

  • 时间限制:1000ms
  • 空间限制:131072K
  • 语言限制:C语言

蒜头君在纸上写了一个串,只包含'('')'。一个'('能唯一匹配一个')',但是一个匹配的'('必须出现在')'之前。请判断蒜头君写的字符串能否括号完全匹配,如果能,输出配对的括号的位置(匹配的括号不可以交叉,只能嵌套)。

输入格式

一行输入一个字符串只含有'('')',输入的字符串长度不大于 5000050000。

输出格式

如果输入括号不能匹配,输出一行"No",否则输出一行"Yes",接下里若干行每行输出 22 个整数,用空格隔开,表示所有匹配对的括号的位置(下标从 11 开始)。你可以按照任意顺序输出。

特判说明

本题答案不唯一,符合要求的答案均正确

样例输入1
(())
样例输出1
Yes
1 4
2 3
样例输入2
()()
样例输出2
Yes
1 2
3 4

题解:从左往右扫一遍括号序列,如果遇到一个(,那把当前下标压入栈中,如果遇到),从栈中弹出一个左括号的位置,存入答案中。如果当前栈为空,就说明括号序列不合法。扫完序列后,栈不为空,也说明括号序列不合法。

我的答案

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

#define MAXN 50000

typedef struct {
    int data[MAXN];
    int top;
} Stack;

void initStack(Stack *s) {
    s->top = -1;
}

int isStackEmpty(Stack *s) {
    return s->top == -1;
}

void push(Stack *s, int x) {
    s->data[++s->top] = x;
}

int pop(Stack *s) {
    return s->data[s->top--];
}

char str[MAXN + 5];
int ans[MAXN][2], cnt;

int main() {
    scanf("%s", str + 1);
    int n = strlen(str + 1);
    Stack s;
    initStack(&s);
    for (int i = 1; i <= n; i++) {
        if (str[i] == '(') {
            push(&s, i);
        } else {
            if (isStackEmpty(&s)) {
                printf("No\n");
                return 0;
            }
            int x = pop(&s);
            ans[++cnt][0] = x;
            ans[cnt][1] = i;
        }
    }
    if (!isStackEmpty(&s)) {
        printf("No\n");
        return 0;
    }
    printf("Yes\n");
    for (int i = 1; i <= cnt; i++) {
        printf("%d %d\n", ans[i][0], ans[i][1]);
    }
    return 0;
}

网页跳转

  • 时间限制:1500ms
  • 空间限制:131072K
  • 语言限制:C语言

蒜头君每天都在用一款名为“蒜厂浏览器”的软件。在这个浏览器中,一共三种操作:打开页面、回退和前进。它们的功能如下:

  • 打开页面:在地址栏中输入网址,并跳转到网址对应的页面;
  • 回退:返回到上一次访问的页面;
  • 前进:返回到上次回退前的页面,如果上一次操作是打开页面,那么将无法前进。

现在,蒜头君打开浏览器,进行了一系列操作,你需要输出他每次操作后所在页面的网址。

输入格式

第一行输入一个整数 n(0<n≤100000),表示蒜头君的操作次数。

接下来一共 n 行,每行首先输入一个字符串,如果是VISIT,后面接着输入一个不含有空格和换行的网址(网址长度小于 100),表示蒜头君在浏览器地址栏中输入的网址;如果是BACK,表示蒜头君点击了回退按钮;如果是FORWARD,表示蒜头君点击了前进按钮。

输出格式

对于每次操作,如果蒜头君能操作成功,输出蒜头君操作之后的网址,否则输出Ignore假设蒜头君输入的所有网址都是合法的

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
10
VISIT https://www.jisuanke.com/course/476
VISIT https://www.taobao.com/
BACK
BACK
FORWARD
FORWARD
BACK
VISIT https://www.jisuanke.com/course/429
FORWARD
BACK
样例输出
https://www.jisuanke.com/course/476
https://www.taobao.com/
https://www.jisuanke.com/course/476
Ignore
https://www.taobao.com/
Ignore
https://www.jisuanke.com/course/476
https://www.jisuanke.com/course/429
Ignore
https://www.jisuanke.com/course/476

题解:需要开两个栈,st1存放当前浏览的记录,st2存放当前回退的记录。

  • 如果是访问操作,清空st2,然后把新的网址放入st1并输出该网址
  • 如果是回退操作,只有在st1的大小至少为 22 时才能回退。把st1的栈顶网址取出,放入st2中,然后输出st1栈顶的网址
  • 如果是前进操作,只有在st2不为空时才能前进,弹出st2的栈顶元素并输出,然后放入st1中。

我的答案

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

#define MAX_URL_LEN 100
#define MAX_N 100000

typedef struct Stack {
    char urls[MAX_N][MAX_URL_LEN];
    int top;
} Stack;

void stack_init(Stack* stack) {
    stack->top = -1;
}

int stack_is_empty(Stack* stack) {
    return stack->top == -1;
}

int stack_is_full(Stack* stack) {
    return stack->top == MAX_N - 1;
}

void stack_push(Stack* stack, char* url) {
    if (!stack_is_full(stack)) {
        strcpy(stack->urls[++stack->top], url);
    }
}

char* stack_pop(Stack* stack) {
    if (!stack_is_empty(stack)) {
        return stack->urls[stack->top--];
    }
    return NULL;
}

char* stack_top(Stack* stack) {
    if (!stack_is_empty(stack)) {
        return stack->urls[stack->top];
    }
    return NULL;
}

int main() {
    int n;
    char operation[10], url[MAX_URL_LEN];
    Stack back_stack, forward_stack;

    stack_init(&back_stack);
    stack_init(&forward_stack);

    scanf("%d", &n);
    while (n--) {
        scanf("%s", operation);
        if (strcmp(operation, "VISIT") == 0) {
            scanf("%s", url);
            stack_push(&back_stack, url);
            printf("%s\n", url);
            stack_init(&forward_stack);
        } else if (strcmp(operation, "BACK") == 0) {
            if (back_stack.top < 1) {
                printf("Ignore\n");
            } else {
                stack_push(&forward_stack, stack_pop(&back_stack));
                printf("%s\n", stack_top(&back_stack));
            }
        } else if (strcmp(operation, "FORWARD") == 0) {
            if (stack_is_empty(&forward_stack)) {
                printf("Ignore\n");
            } else {
                char *next_url = stack_pop(&forward_stack);
                stack_push(&back_stack, next_url);
                printf("%s\n", next_url);
            }
        }
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值