前言:本文章适合考研计算机科学与技术的机试环节准备,较为适合想打算法竞赛的,有一定编程基础的人尝试入门的积累学习。
题目
1.杭电Problem - 2024
答案:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main() {
int n;
char s[60]; // 长度不超过50,再预留一些空间
// 读取测试实例个数
scanf("%d", &n);
getchar(); // 读取换行符
while (n--) {
// 读取整行字符串,包括可能的空格
fgets(s, sizeof(s), stdin);
// 去掉末尾换行符
int len = strlen(s);
if(len > 0 && s[len - 1] == '\n')
s[len - 1] = '\0';
int valid = 1;
// 空串不是合法标识符
if (s[0] == '\0') {
valid = 0;
}
else {
// 判断首字符:必须是字母或下划线
if (!(isalpha(s[0]) || s[0] == '_')) {
valid = 0;
}
// 其余字符必须是字母、数字或下划线
else {
for (int i = 1; s[i] != '\0'; i++) {
if (!(isalnum(s[i]) || s[i] == '_')) {
valid = 0;
break;
}
}
}
}
// 输出结果
if (valid)
printf("yes\n");
else
printf("no\n");
}
return 0;
}
知识点:
-
关于 getchar()
调用getchar();
是为了读取掉在使用scanf("%d", &n)
后残留在输入缓冲区中的换行符(回车键)。如果不这样做,后续调用fgets
时可能会首先读取到这个换行符,从而导致读取到空行或不完整的输入。 -
关于 fgets
fgets
从标准输入中读取字符直到遇到换行符或达到指定的字符数为止。它会将换行符也包含在读取的字符串中(前提是缓冲区有足够空间),这就是为什么在读取后常常需要检查并将末尾的换行符替换为字符串终止符。 -
关于 stdin
stdin
是标准输入流的文件指针。默认情况下,它与键盘输入相关联,也可以通过重定向从文件中读取数据。使用fgets(s, sizeof(s), stdin)
表示从标准输入流中读取数据到字符串s
中。 -
将换行符替换为字符串结束符的目的
将末尾的换行符替换为'\0'
(字符串结束符)可以方便后续对字符串的处理。这样处理后,字符串就不会包含额外的换行符,方便进行字符串比较、输出等操作。
数据结构:
1.单链表
题目:
C++版:
#include <iostream>
using namespace std;
const int N = 100010;
// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 将x插到头结点
void add_to_head(int x)
{
e[idx] = x, ne[idx] = head, head = idx ++ ;
}
// 将x插到下标是k的点后面
void add(int k, int x)
{
e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}
// 将下标是k的点后面的点删掉
void remove(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
int m;
cin >> m;
init();
while (m -- )
{
int k, x;
char op;
cin >> op;
if (op == 'H')
{
cin >> x;
add_to_head(x);
}
else if (op == 'D')
{
cin >> k;
if (!k) head = ne[head];
else remove(k - 1);
}
else
{
cin >> k >> x;
add(k - 1, x);
}
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
cout << endl;
return 0;
}
C版:
#include <stdio.h>
#include <string.h>
#define N 100010
int head, e[N], ne[N], idx;
// 初始化链表,将 head 置为 -1,表示空链表
void init() {
head = -1;
idx = 0;
}
// 将 x 插入到链表头部
void add_to_head(int x) {
e[idx] = x;
ne[idx] = head;
head = idx++;
}
// 将 x 插入到下标为 k 的节点后面
void add(int k, int x) {
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx++;
}
// 删除下标为 k 的节点之后的那个节点
void remove_node(int k) {
ne[k] = ne[ ne[k] ];
}
int main() {
int m;
scanf("%d", &m);
init();
while(m--) {
int k, x;
char op;
scanf(" %c", &op); // 格式化字符串前加空格,跳过空白字符
if (op == 'H') {
scanf("%d", &x);
add_to_head(x);
} else if (op == 'D') {
scanf("%d", &k);
if (k == 0)
head = ne[head];
else
remove_node(k - 1);
} else { // 其它操作,插入操作:在下标为 k 的节点后面插入 x
scanf("%d%d", &k, &x);
add(k - 1, x);
}
}
// 输出链表中的所有元素
for (int i = head; i != -1; i = ne[i])
printf("%d ", e[i]);
printf("\n");
return 0;
}
2.双链表
题目:
C++版:
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <set>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1e7;
int m;
int e[N], l[N], r[N], id;
void add(int a, int x) {
e[id] = x;
l[id] = a, r[id] = r[a];
l[r[a]] = id, r[a] = id++;
}
void sc(int a) {
l[r[a]] = l[a];
r[l[a]] = r[a];
}
int main() {
cin >> m;
r[0] = 1, l[1] = 0;
id = 2;
while (m--) {
string op;
cin >> op;
int k, x;
if (op == "L")
{
cin >> x;
add(0, x);
}
else if (op == "R")
{
cin >> x;
add(l[1], x);
}
else if (op == "D")
{
cin >> k;
sc(k+1);
}
else if (op == "IL")
{
cin >> k >> x;
add(l[k + 1], x);
}
else
{
cin >> k >> x;
add(k + 1, x);
}
}
for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
cout << endl;
return 0;
}
C版:
#include <stdio.h>
#include <string.h>
#define N 10000000
int m;
int e[N], l[N], r[N], id;
// 将 x 插入到下标为 a 的节点后面
void add(int a, int x) {
e[id] = x;
l[id] = a;
r[id] = r[a];
l[r[a]] = id;
r[a] = id++;
}
// 删除下标为 a 的节点(把它从链表中断开)
void sc(int a) {
l[r[a]] = l[a];
r[l[a]] = r[a];
}
int main() {
int i, k, x;
char op[5]; // 操作字符串 "L", "R", "D", "IL", "IR"
scanf("%d", &m);
// 初始化双链表
// 设头结点下标为 0,尾结点下标为 1
r[0] = 1;
l[1] = 0;
id = 2;
while (m--) {
scanf("%s", op);
if (strcmp(op, "L") == 0) {
scanf("%d", &x);
add(0, x);
} else if (strcmp(op, "R") == 0) {
scanf("%d", &x);
add(l[1], x);
} else if (strcmp(op, "D") == 0) {
scanf("%d", &k);
// 删除第 k 个插入的节点,对应下标为 k+1
sc(k + 1);
} else if (strcmp(op, "IL") == 0) {
scanf("%d%d", &k, &x);
add(l[k + 1], x);
} else if (strcmp(op, "IR") == 0) {
scanf("%d%d", &k, &x);
add(k + 1, x);
}
}
// 输出链表中所有节点的值(从头结点的后继开始,到尾结点之前)
for (i = r[0]; i != 1; i = r[i])
printf("%d ", e[i]);
printf("\n");
return 0;
}
3.滑动窗口(单调队列)
题目:
C++/C版:
#include <iostream>
using namespace std;
const int N = 1000010;
int a[N], q[N];//a表示原数组,q表示单调队列(内存下标)
int main()
{
int n, k;
scanf("%d%d", &n, &k);//n为数组长度,k为滑动窗口长度
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int hh = 0, tt = -1;//hh为队头,tt为队尾
for (int i = 0; i < n; i++)
{
if (hh <= tt && i - k + 1 > q[hh])//判断窗口是否滑过
{
hh++;
}
while (hh <= tt && a[q[tt]] >= a[i])//如果队尾的数大于当前的数,则将队尾的数覆盖
{
tt--;
}
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
hh = 0, tt = -1;
for (int i = 0; i < n; i++)
{
if (hh <= tt && i - k + 1 > q[hh]) hh++;
while (hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}
4.KMP
链接: