学习小结
链表
对于链表的学习,首先要大体对指针和结构体有一定的认识。
链表使用时一般先用结构体定义一下,一般包括两部分 数据域和指针域 ,数据域顾名思义就是储存数据的,指针域是储存指向下一个结构的指针,如下
typedef struct SListNode
{
int data;
struct SListNode* next;
}SLND;
这两种看起来是有一些不同的,但是第一种主要是方便后面的定义(我个人喜欢第一个)。具体区别可以先学一下结构体就明白了。
struct SListNode
{
int data;
struct SListNode* next;
};
准备工作做好了接下来就是对链表进行操作了。
第一步:肯定是要初始化!!!!(也可以说是搭建结点)
//建立结点
SLND* CreateSListNode(int x)
{
SLND* NewNode = (SLND*)malloc(sizeof(SLND));
NewNode->data = x;
NewNode->next = NULL;
return NewNode;
}
第二步:搭好结点了就可以准备把这些结点穿起来 。(也可以说是在每个结点的结尾插入结点)
//尾插结点
void SListPushBack(SLND** head, int x)
{
SLND* New = CreateSListNode(x);
if (*head == NULL)
*head = New;
else
{
SLND* t = *head;
while (t->next != NULL)
t = t->next;
t->next = New;
}
}
这样一个简单的链表就搭起来了,但是通常解题的时候不会这么简单 ,(会用各种条件限制)
所以现在说一下几个小方法
头插节点(比如说开头忘了加一个结点)
//头插节点
void SListPushFront(SLND** head, int x)
{
SLND* New = CreateSListNode(x);
New->next = *head;
*head = New;
}
尾删结点(结尾的结点用不到了,就需要删掉,防止野指针的出现,出现危险)
一定一定一定注意把结尾的指针释放掉(用free函数)因为野指针真的很危险!!!!
//尾删结点
void SListPopBack(SLND** head)
{
//没有结点,空链表
if (*head == NULL)
{
return;
}
//只有一个结点
else if ((*head)->next == NULL)
{
free(*head);
*head = NULL;
}
//多个结点
else
{
SLND* prev = NULL;
SLND* t = *head;
while (t->next != NULL)
{
prev = t; //找到倒数第二个结点的指针
t = t->next;
}
free(t); //释放最后一个结点
prev->next = NULL; //将倒数第二个指针置空,防止成为野指针
}
}
头删结点
//头删结点
void SListPopFront(SLND** head)
{
if (*head == NULL)
return;
else
{
SLND* next = NULL;
next = (*head)->next;
free(*head);
*head = next;
}
}
下面是用完整是代码实现插入结点
typedef struct node
{
int data;
struct node* next;
}Nd;
int main()
{
Nd* p = NULL, * q = NULL, * head = NULL, * t = NULL;
int n = 0, a = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a);
p = (Nd*)malloc(sizeof(Nd));
p->data = a;
p->next = NULL;
if (head == NULL)
head = p;
else
q->next = p;
q = p;
}
scanf("%d", &a);
t = head;
while (t != NULL)
{
if (t->next || t->next->data > a)
{
p = (Nd*)malloc(sizeof(Nd));
p->data = a;
p->next = t->next;
t->next = p;
break;
}
t = t->next;
}
t = head;
while (t != NULL)
{
printf("%d ", t->data);
t = t->next;
}
}
中间插入结点的核心代码就是
大体就是从头开始查找目标位置,找到之后用一个中间量连接起来就可以
scanf("%d", &a);
t = head;
while (t != NULL)
{
if (t->next || t->next->data > a)
{
p = (Nd*)malloc(sizeof(Nd));
p->data = a;
p->next = t->next;
t->next = p;
break;
}
t = t->next;
}
附一个之前写的关于学生成绩统计的代码吧,可以大体看一下。(这个主要是了解如何动态申请空间)
宏定义
#define TableHead "学号\t 姓名\t 性别\t 出生日期\t 语文\t 数学\t 英语\t 平均\n"
#define BirthdayOutput stu->bthd.year, stu->bthd.month, stu->bthd.day
#define BirthdayInput &stu->bthd.year, &stu->bthd.month, &stu->bthd.day
typedef struct date
{
int year;
int month;
int day;
} DATA;
typedef struct student
{
int stuID;
char stuName[20];
char gender;
DATA bthd;
int Chin;
int Math;
int Engl;
float average;
struct student* next; //节点数据类型
} STUDENT;
STUDENT inputStu(STUDENT* stu)
{
printf("学号(2023001): ");
scanf("%d", &stu->stuID);
printf("姓名: ");
scanf("%s", stu->stuName);
getchar();
printf("性别(M,F): ");
scanf("%c", &stu->gender);
printf("出生日期(yy-mm-dd): ");
scanf("%d-%d-%d", BirthdayInput);
printf("语文(0~100): ");
scanf("%d", &stu->Chin);
printf("数学(0~100): ");
scanf("%d", &stu->Math);
printf("英语(0~100): ");
scanf("%d", &stu->Engl);
stu->average = (float)(stu->Chin + stu->Math + stu->Engl) / 3;
return *stu;
}
void outputStuTable(STUDENT* stu)
{
printf("%07d\t", stu->stuID);
printf("%s\t", stu->stuName);
printf("%c\t", stu->gender);
printf("%4d-%02d-%02d\t", BirthdayOutput);
printf("%d\t", stu->Chin);
printf("%d\t", stu->Math);
printf("%d\t", stu->Engl);
printf("%.2f\n", stu->average);
}
void sortByAverage(STUDENT* stu[], int n)
{
int i, j;
STUDENT* temp;
for (i = 0; i < n; i++)
for (j = 0; j < n - i - 1; j++)
if (stu[j]->average < stu[j + 1]->average)
{
temp = stu[j];
stu[j] = stu[j + 1];
stu[j + 1] = temp;
}
}
int main()
{
STUDENT* stu[10];
STUDENT* p = NULL;
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
p = (STUDENT*)malloc(sizeof(STUDENT));
if (p == NULL)
{
printf("error");
}
inputStu(p);
stu[i] = p;
}
sortByAverage(stu, n);
printf(TableHead);
for (int i = 0; i < n; i++)
{
outputStuTable(stu[i]);
}
for (int i = 0; i < n; i++)
{
free(stu[i]);
}
return 0;
}
练习题
P7505 「Wdsr-2.5」小小的埴轮兵团
# 「Wdsr-2.5」小小的埴轮兵团
## 题目背景
杖刀偶磨弓是埴轮兵团的首长。
作为埴轮兵长,训练埴轮兵团是很平常的事情。
## 题目描述
磨弓下达命令让埴轮们站成一行。不妨认为它们站在了一个数轴上,每个埴轮的位置就是它脚下数轴的数字。磨弓会告诉你,第 $i$ 个埴轮的位置为 $a_i$ 。**不保证** $\bm {a_i}$ **升序**。
数轴的长度是有限制的,具体的范围是 $[-k,k]$ 。也就是说,如果某个埴轮移出了这个范围,它就脱离了这个队列了,并且不会再次回到队列当中。
为了训练埴轮,磨弓给埴轮们下达了 $m$ 个指令,有以下 3 种:
- 指令 1:**全体埴轮**向数轴的正方向移动 $x$ 个单位长度。
- 指令 2:**全体埴轮**往数轴的反方向移动 $x$ 个单位长度。
- 指令 3:依次报数,统计目前队列里一共有多少个埴轮。
但是磨弓发现,埴轮兵团的大小实在是太大了,以至于执行这些操作变得非常缓慢。尽管如此,磨弓仍然希望你告诉她所有指令 3 的结果。
## 输入格式
第一行共有 $3$ 个整数 $n, m, k$,含义如题面所示。
第二行共有 $n$ 个整数 $a_1, a_2, \cdots, a_n$,表示每个埴轮的位置。
接下来 $m$ 行,有 $1$ 或者 $2$ 个正整数,描述一条指令。首先是一个整数 $\operatorname{op}$,表示这条指令的类型。如果 $1 \leq \operatorname{op} \leq 2$,接下来还会输入一个整数 $x$。
## 输出格式
对于每条指令 3 ,输出一个整数,表示目前还在队列中的埴轮的数目。
## 样例 #1
### 样例输入 #1
```
3 4 3
-1 1 2
2 3
3
1 5
3
```
### 样例输出 #1
```
2
1
```
## 提示
#### 样例 1 说明
一共有三个埴轮。初始时,它们的站位分别是 $[-1,1,2]$ 。
- 第一次操作后,所有埴轮向左移动 $3$ 格,位置变成了 $[\underline{\bm{-4}},-2,-1]$ 。第一个埴轮被移出了数轴。
- 第二次操作后,输出当前的埴轮数目,为 $2$ 个。
- 第三次操作后,所有埴轮向右移动 $5$ 格,位置变成了 $[3,\underline \bm4]$ ,第二个埴轮被移出了数轴。
- 第四次操作后,输出当前的埴轮数目,为 $1$ 个。
#### 样例 2, 3
见下发附件。
#### 数据规模与约定
- 对于 $30\%$ 的数据,$1 \leq n, m \leq 5\times 10^3$;
- 对于另外 $20\%$ 的数据,$1\le k\le 500$;
- 对于 $100\%$ 的数据,$1 \leq n, m \leq 3\times 10^5$,$1 \leq k, x \leq 2 \times 10^9$,$-k \le a_i \le k$ 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,m,l=1,r,op;
ll k,a[N],x,p;
int main()
{
cin>>n>>m>>k;
r=n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1);//排序
for(int i=1;i<=m;i++)
{
cin>>op;
if(op==3)//操作3
{
cout<<r-l+1<<endl;
continue;
}
cin>>x;
if(op==1)//操作1
{
p+=x;
while(a[r]+p>k&&l<=r)//队尾出队
r--;
}
else//操作2
{
p-=x;
while(a[l]+p<-k&&l<=r)//队头出队
l++;
}
}
return 0;
}
P1981 [NOIP2013 普及组] 表达式求值
# [NOIP2013 普及组] 表达式求值
## 题目背景
NOIP2013 普及组 T2
## 题目描述
给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。
## 输入格式
一行,为需要你计算的表达式,表达式中只包含数字、加法运算符 `+` 和乘法运算符 `*`,且没有括号,所有参与运算的数字均为 $0$ 到 $2^{31}-1$ 之间的整数。
输入数据保证这一行只有 `0123456789+*` 这 $12$ 种字符。
## 输出格式
一个整数,表示这个表达式的值。
注意:当答案长度多于 $4$ 位时,请只输出最后 $ 4$ 位,前导 $ 0$ 不输出。
## 样例 #1
### 样例输入 #1
```
1+1*3+4
```
### 样例输出 #1
```
8
```
## 样例 #2
### 样例输入 #2
```
1+1234567890*1
```
### 样例输出 #2
```
7891
```
## 样例 #3
### 样例输入 #3
```
1+1000000003*1
```
### 样例输出 #3
```
4
```
## 提示
对于 $30\%$ 的数据,$0≤$ 表达式中加法运算符和乘法运算符的总数 $≤100$。
对于 $80\%$ 的数据,$0≤$ 表达式中加法运算符和乘法运算符的总数 $≤1000$。
对于 $100\%$ 的数据,$0≤$ 表达式中加法运算符和乘法运算符的总数 $≤100000$。
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include<stdbool.h>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cstring>
#include<bits/stdc++.h>
int m = 10000;
int t = 0, x = 0, sum = 0;
char c;
int main()
{
scanf("%d", &t); //先读取一个数字
while (scanf("%c",&c) && c!='\n')
{
scanf("%d", &x);
if (c == '*')
t = t * x % m;
else
{
sum = (sum + t) % m;
t = x;
}
}
printf("%d\n", (sum + t) % m);
return 0;
}
P1219 [USACO1.5] 八皇后 Checker Challenge
# [USACO1.5] 八皇后 Checker Challenge
## 题目描述
一个如下的 $6 \times 6$ 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
![](https://cdn.luogu.com.cn/upload/image_hosting/3h71x0yf.png)
上面的布局可以用序列 $2\ 4\ 6\ 1\ 3\ 5$ 来描述,第 $i$ 个数字表示在第 $i$ 行的相应位置有一个棋子,如下:
行号 $1\ 2\ 3\ 4\ 5\ 6$
列号 $2\ 4\ 6\ 1\ 3\ 5$
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 $3$ 个解。最后一行是解的总个数。
## 输入格式
一行一个正整数 $n$,表示棋盘是 $n \times n$ 大小的。
## 输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
## 样例 #1
### 样例输入 #1
```
6
```
### 样例输出 #1
```
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
```
## 提示
【数据范围】
对于 $100\%$ 的数据,$6 \le n \le 13$。
题目翻译来自NOCOW。
USACO Training Section 1.5
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include<stdbool.h>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cstring>
#include<bits/stdc++.h>
using namespace std;
int a[100] = { 0 }, b[100] = { 0 }, c[100] = { 0 }, d[100] = { 0 }; //c 左下到右上 d 左上到右下
int total = 0, n = 0;
void print()
{
if (total <= 2)
{
for (int i = 1; i <= n; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
total++;
}
void queen(int i)
{
if (i > n) {
print();
return;
}
else
{
for (int j = 1; j <= n; j++)
{
if ((!b[j]) && (!c[i + j]) && (!d[i - j + n]))
{
a[i] = j;
b[j] = 1;
c[i + j] = 1;
d[i - j + n] = 1;
queen(i + 1);
b[j] = 0;
c[i + j] = 0;
d[i - j + n] = 0;
}
}
}
}
int main()
{
cin >> n;
queen(1);
cout << total <<endl;
return 0;
}