Files
project1/第07课-数组运算/教案-教师版.md

284 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第9课数组计数法
## 教学目标
- 理解“数值作下标、次数存数组”的计数思想
- 掌握数组计数的核心写法 `cnt[x]++`
- 能用计数数组完成频次统计、最高频查找和去重升序输出
- 知道数组计数法的适用条件和下标范围限制
## 核心考点:数值映射为下标的数组计数思想
### 考什么
题目会给出一组范围明确的整数,要求统计频次、找出现次数最多的数、判断某数是否出现,或去重后按序输出。关键是不用两两比较,而是让每个数直接找到自己的计数位置。
### 必须理解
当数值 x 能作为合法下标时,用 `cnt[x]` 表示 x 出现的次数。每读到一次 x就执行 `cnt[x]++`。统计结束后,遍历计数数组即可获得所有频次;因为下标从小到大遍历,输出天然有序。使用前必须根据数据范围确定数组大小并清零。若存在负数,可在范围较小时整体偏移;若数值范围极大,就不适合直接开计数数组。
### 核心写法
```cpp
int cnt[101] = {};
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x]++;
}
for (int value = 0; value <= 100; value++) {
if (cnt[value] > 0) {
cout << value << ' ' << cnt[value] << endl;
}
}
```
这里下标 `value` 代表具体数值,数组元素 `cnt[value]` 代表次数,两者含义不能混淆。
### 常见错误
- 数值范围到100却只开 `cnt[100]`无法访问下标100。
- 计数数组未清零,初始垃圾值会参与累加。
- 输入可能为负数或超范围,仍直接作为下标导致越界。
- 输出最高频数字时混淆“数字本身”和“出现次数”。
### 判断是否掌握
能根据数据范围确定计数数组大小,能解释 `cnt[x]++` 的含义,并能通过一次计数、一次遍历完成频次查询、众数或去重输出。
---
## 课前认识:什么是数组计数法
如果输入的数字都在 `0~100` 之间,可以准备一个 `cnt[101]` 数组:
- 数字 `x` 出现一次,就执行 `cnt[x]++`
- `cnt[3]` 表示数字 3 出现了几次
- `cnt[100]` 表示数字 100 出现了几次
例如输入 `3 1 3 2 1 3`,计数后有:
| 数字(下标) | 0 | 1 | 2 | 3 |
|---|---:|---:|---:|---:|
| 次数cnt | 0 | 2 | 1 | 3 |
> 数组计数法的核心,就是把数字直接放到对应编号的“小格子”里做记号。
---
## 题目一:统计数字出现次数
### 题目描述
输入 n 个 `0~100` 之间的整数,按数字从小到大的顺序,输出每个出现过的数字及其出现次数。
### 核心代码(一句话)
> 每读到一个数字 `x`,就让对应位置加一:`cnt[x]++`。
### 自然语言思路
第一步,准备 `cnt[101]`,把每个位置都初始化为 0。
第二步,循环读入 n 个数字。读到 `x`,就在编号为 x 的格子里加一次,也就是 `cnt[x]++`
第三步,从 0 遍历到 100。若 `cnt[i] > 0`,说明数字 i 出现过,输出 i 和它的次数。
### 伪代码
```text
1. 输入 n
2. 创建并清零计数数组 cnt[101]
3. 重复 n 次:
4. 输入 x
5. cnt[x] 加 1
6. 从 i = 0 遍历到 100
7. 如果 cnt[i] > 0
8. 输出 i 和 cnt[i]
```
### 真实代码
```cpp
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int cnt[101] = {};
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x]++;
}
for (int i = 0; i <= 100; i++) {
if (cnt[i] > 0) {
cout << i << ":" << cnt[i] << endl;
}
}
return 0;
}
```
### 注意点
- `int cnt[101] = {};` 会把整个数组清零。
- 数值范围是 `0~100`,所以数组需要 101 个位置。
- 输出时只输出 `cnt[i] > 0` 的数字。
- 输入值不能超出数组下标范围,否则会越界。
---
## 题目二:寻找出现次数最多的数字
### 题目描述
输入 n 个 `0~100` 之间的整数,输出出现次数最多的数字。如果多个数字出现次数相同,输出其中较小的数字。
### 核心代码(一句话)
> 先用 `cnt[x]++` 统计次数,再从小到大打擂台;只有次数严格更多时才更换答案。
### 自然语言思路
第一步,先像题目一一样统计每个数字出现的次数。
第二步,假设数字 0 暂时是擂主,用 `bestValue` 保存擂主数字,用 `bestCount` 保存它的出现次数。
第三步,从数字 1 遍历到 100。如果 `cnt[i] > bestCount`,说明 i 出现得更多,让 i 成为新擂主。
第四步,如果次数相同,不更换擂主。因为我们从小到大遍历,先成为擂主的数字更小,正好满足并列时取较小值。
### 伪代码
```text
1. 输入 n统计 cnt
2. bestValue = 0
3. bestCount = cnt[0]
4. 从 i = 1 遍历到 100
5. 如果 cnt[i] > bestCount
6. bestValue = i
7. bestCount = cnt[i]
8. 输出 bestValue
```
### 真实代码
```cpp
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int cnt[101] = {};
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x]++;
}
int bestValue = 0;
int bestCount = cnt[0];
for (int i = 1; i <= 100; i++) {
if (cnt[i] > bestCount) {
bestValue = i;
bestCount = cnt[i];
}
}
cout << bestValue << endl;
return 0;
}
```
### 注意点
- 题目保证 `n >= 1`,因此一定存在答案。
- 判断条件必须是 `>`,不能写成 `>=`;否则并列时会留下较大的数字。
- `bestValue` 保存数字,`bestCount` 保存次数,不要混淆。
- 这道题把第4课的“打擂台”和本课的数组计数结合了起来。
---
## 题目三:去重后升序输出
### 题目描述
输入 n 个 `0~100` 之间的整数,去掉重复数字,并按从小到大的顺序输出。
### 核心代码(一句话)
> 计数后从小到大遍历下标,`cnt[i] > 0` 就输出 i每个数字只输出一次。
### 自然语言思路
第一步,用计数数组记录每个数字是否出现过。
第二步,从 0 遍历到 100。数组下标本身就是数字而且遍历顺序天然是从小到大。
第三步,只要 `cnt[i] > 0`,就输出一次 i。不管它原来出现两次还是十次都只输出一次因此完成去重。
### 伪代码
```text
1. 输入 n统计 cnt
2. 从 i = 0 遍历到 100
3. 如果 cnt[i] > 0
4. 输出 i
```
### 真实代码
```cpp
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int cnt[101] = {};
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x]++;
}
bool first = true;
for (int i = 0; i <= 100; i++) {
if (cnt[i] > 0) {
if (!first) cout << ' ';
cout << i;
first = false;
}
}
cout << endl;
return 0;
}
```
### 注意点
- 遍历计数数组时,下标本身按从小到大变化,因此输出天然有序。
- 去重只关心“是否出现”,所以判断 `cnt[i] > 0` 即可。
- `first` 用来控制空格,避免行首或行尾出现多余空格。
- 如果数字范围非常大,例如到 `10^9`,就不适合直接开这么大的计数数组。
---
## 本课打油诗
> 数值下标来对号,
> 每次出现次数高。
> 从小到大逐格找,
> 统计去重一招好。
---
## 课后作业
1. 输入 n 个 `0~50` 的整数,再输入一个数字 q输出 q 出现了多少次。
2. 输入 n 个 `0~100` 的整数,输出出现次数最少且出现过的数字;并列时输出较小者。
3. 输入 n 个学生的分数(`0~100`依次输出每个分数段的人数不及格、60~79、80~89、90~100。
> 提示:先判断题目中的数字范围,再决定计数数组要开多大。