排好序,再选对工具去查找和计数
这一课只练一件事:拿到一批数据后,先看清 ,再决定用、先后扫描,还是复用。必会:判断何时该用排序、处理好、按取值范围选出数组或映射方案。最小示例:6 名学生的成绩 90 78 85 90 85 90,分数范围 0 <= 分数 <= 100,1 <= n <= 1000,要求统计每个分数出现次数,并输出出现次数最多的分数。样例追踪:排序后是 78 85 85 90 90 90,扫描一遍数出 78 出现 1 次、85 出现 2 次、90 出现 3 次,最高频分数是 90。边界是 n=1(这唯一一个分数本身就是最高频,次数为 1),以及多个分数并列最高频时按“取数值更小的分数”统一规则。建议掌握:分数范围很小(0~100)时可以直接开计数数组,比排序更省事。范围变得很大(比如到 10 的 9 次方)时改用映射结构是拓展。这里不会在浏览器里运行 C++。
任务 1:先选出该用计数数组还是排序后扫描
决定了工具选择:范围小的整数适合直接开 ;范围较大或不是整数下标时,先 再扫描更通用。点不认识的词可看基础解释。
必会:能说出分数范围 0~100 时可以直接用计数数组统计次数;不方便用计数数组时要先排序,让相同的分数挨在一起。这里不会在浏览器里运行 C++,只练习选择和走过程。
分数范围是 0~100,要统计每个分数出现次数,该怎么做最省事?数据范围 · 必会
如果不方便开计数数组,想找出现次数最多的分数,还能怎么做?处理重复 · 必会
任务 2:按排序、扫描、记录三步统计出现次数
最小示例:6 名学生成绩 90 78 85 90 85 90,要统计出现次数最多的分数。按顺序点三步:先 把分数排好,再顺序扫描,累计连续相同的次数(这一步在处理 ),最后记录当前出现次数最多的分数。这里不会在浏览器里运行 C++。
必会:能解释每一步为什么这样做。边界:不要跳过“扫描”直接猜答案。排序后如果还要按分数查某个具体值是否存在,可以直接复用 ,不用重新写一遍扫描逻辑。
- // 排序:90 78 85 90 85 90 → 78 85 85 90 90 90
- // 扫描:78(1 次) 85(2 次) 90(3 次),相邻相同就累加,不同就重新计数
- // 记录:90 的连续次数 3 大于已记录的最高频,更新最高频分数为 90
- // 若需查某个具体分数是否存在,可复用二分查找
三步走完了,现在把“扫描时统计次数”落成一行代码:分数范围只有 0~100,读入时就用 同步统计,比排完序再回头数更省事。下面是只读脚手架,只留一空给你敲键盘;这里不会在浏览器里运行 C++。
- // cnt[v] 记录分数 v 出现过几次,分数范围 0~100 开得下
- int cnt[101] = {0};
- int n; cin >> n;
- for (int i = 1; i <= n; i += 1) {
- int score; cin >> score;
- // 读入时同步统计这个分数
- }
- int best = 0;
- for (int v = 1; v <= 100; v += 1) { if (cnt[v] > cnt[best]) best = v; } // 并列时保留数值更小的分数
- cout << best << " " << cnt[best];
cnt[score]++; // 读入这个分数时,把它对应的格子加一
任务 3:确定处理思路,并认出两个边界
建议掌握:分数范围很小(0~100)时,可以在读入时同步用计数数组统计,比先排序再扫描更省事;最后遍历计数数组找出现次数最多的下标即可。范围变得很大(比如到 10 的 9 次方)时,计数数组开不下,需要改用映射结构(如 map),这是拓展内容。
必会 / 建议掌握 / 拓展:必会是判断何时用排序、处理重复元素、按范围选择数组或映射方案。建议掌握是范围很小时优先用计数数组。改用 map 等映射结构是拓展,现在不用当硬门槛。
- // 思路:读入时同步 cnt[分数]++,最后遍历 cnt 找次数最大的下标
- // 边界 1:n=1 时,这唯一一个分数就是最高频,次数为 1
- // 边界 2:多个分数并列最高频时,统一取数值更小的分数
- // 范围很大时改用 map 等映射结构是拓展
先选出这题该用什么处理思路:
再选出两个边界:
任务 4:随机小测,看看你是否真的懂了
每次从本课题库抽出三道不同的选择题。题目与解析已通过校验,答完后看解析,不要只看分数;拿不准就回到对应任务再看一遍。