1449 字
7 分钟
阅读量加载中
用仓颉解决两道数组题:计数与最少会议室

两道题的迁移记录:统计一串数字里每个数字出现的次数,以及给定若干会议区间求最少需要几间会议室。Python 里用字典和动态列表就够了;换到仓颉后卡在三处——字典还没用熟、字符不能直接转 Int64、数组没有 remove 和 append,于是改成「数组下标当桶」和「差分数组」两条路线。两个关键结论:字符 '0' 的码点是 48,减 48 才是真正的数值;用「开始 +1、结束 −1」的差分数组,可以完全绕开对列表的动态增删。

前置条件#

  • 能编译运行仓颉代码的环境(用 getStdIn() 读标准输入需要 import std.env.*)
  • Python 3,用来对照两种写法的差异
  • 了解数组遍历,以及字符要先转成编码值(整数码点)这件事

统计每个数字出现的次数#

Python 里最直接的办法是字典,取不到就当 0:

# 字典解决统计数字问题
num = input()
count = {}
for i in num:
count[i] = count.get(i, 0) + 1
for j in sorted(count.keys()):
print(f"{j}:{count[j]}")

仓颉的字典我还没用熟,于是换成数组下标当“桶”:数字本身就是下标,槽位里放出现次数。先在 Python 里把思路验证一遍:

# 列表解决统计数字问题
num = input()
count = [0] * 10
for i in num:
count[int(i)] += 1
for j in range(10):
if count[j] == 0:
continue
else:
print(f"{j}:{count[j]}")

两种写法输出一致:

Pasted image 20261002005655.png

迁移到仓颉时卡了一下:它不会把字符自动转成 Int64,强制转换还会报错。查资料后知道字符要先转成它的编码值(整数码点),逐个试出来 '0' 的码点是 48,所以当前字符减 48 再转 Int64,就是它在数组里的正确下标。照 Python 版的思路写出来:

// 数组解决统计数字问题
import std.env.*
main(){
let input = getStdIn().readln().getOrThrow()
var count = Array<Int64>(10, repeat: 0)
for(i in input where i>=48 && i<=57){
let num = Int64(i - 48)
count[num] += 1
}
for (i in 0..=9 where count[i] != 0) {
println("${i}:${count[i]}")
}
}

运行结果:

Pasted image 20261002011014.png

顺带一提,where i>=48 && i<=57 这一段把非数字字符过滤掉了,输入里混了别的字符也不会越界或报错。

求最少会议室数#

先说原本的思路:维护一个「正在占用的会议室结束时间」列表。来一场新会议时,如果它的开始时间不早于列表里最早的结束时间,说明那间会议室已经空出来、可以复用,就把这个最早的结束时间删掉;否则新开一间。列表最终的长度就是最少会议室数。Python 版:

meeting_times = [(0, 600), (200, 700), (650, 1200)]
end_times = []
for start, end in meeting_times:
if end_times:
min_end_time = min(end_times)
if start >= min_end_time:
end_times.remove(min_end_time)
end_times.append(end)
print(len(end_times))

运行结果:

Pasted image 20261002013651.png

问题在于仓颉的数组既没有 remove 也没有 append,这条路走不通。于是改用差分数组:先扫一遍拿到最晚的结束时间,建一个长度为「最晚结束时间 + 1」的数组,在每个会议的开始位置 +1、结束位置 −1,最后从头到尾累加,累加过程中的峰值就是同时进行的会议数上限,也就是最少需要的会议室数。Python 验证:

meeting_times = [(0, 600), (200, 700), (650, 1200)]
last_end_time = 0
for start, end in meeting_times:
last_end_time = max(last_end_time, end)
# 长度加 1 是因为下标从 0 开始:最大键值是 1200,不加 1 的话最大下标只有 1199,写到 1200 会越界
changes = [0] * (last_end_time + 1)
for start, end in meeting_times:
changes[start] += 1
changes[end] -= 1
occupied = 0
rooms = 0
for change in changes:
occupied += change
rooms = max(rooms, occupied)
print(f"最少需要的会议室: {rooms}")

运行结果:

Pasted image 20261002015348.png

同样思路的仓颉版:

main(){
let t = [(0, 600),(200,700),(650,1200)]
var lastEnd = 0
for((start,end) in t){
lastEnd = max(lastEnd, end)
}
let changes = Array<Int64>(lastEnd + 1, repeat: 0)
for ((begin, end) in t) {
changes[begin] += 1
changes[end] -= 1
}
var occupied = 0
var rooms = 0
for (change in changes) {
occupied += change
rooms = max(rooms, occupied)
}
println("最少需要的会议室:${rooms}")
}

运行结果:

Pasted image 20261002015954.png

我在实现时卡住的地方#

仓颉里字符为什么不能直接转 Int64?#

仓颉不会像 Python 的 int() 那样把字符自动当数字。字符要先拿到它的编码值(整数码点),而 '0' 的码点不是 0 而是 48,所以必须减 48 才是真正的数值。直接 Int64(i) 拿到的是码点,不是数字本身。

差分数组怎么就等价于原来的增删列表?#

差分数组把「复用会议室」变成了计数:开始 +1 表示占用一间,结束 −1 表示释放一间。从头累加得到的是「当前时刻正在进行几场会议」,这个累加值的峰值就是同时进行的上限,也就是至少要准备的会议室数。原来靠 remove / append 维持的那个列表长度,本质上就是这个峰值,所以两者结果一致;而差分数组只需要按下标改值,不需要动态增删。

数组长度为什么要「最晚结束时间 + 1」?#

下标从 0 开始,要能访问到 last_end_time 这个位置,长度就得是 last_end_time + 1。用 (0,600),(200,700),(650,1200) 这组数据举例,最晚结束时间是 1200,长度必须是 1201,否则写入 changes[1200] 会越界。

差分数组适合什么场景?#

适合数值范围已知、只对静态数据做区间加减、最后统一求前缀和的场景。代价是要先知道取值范围的上界(这里取最晚结束时间),并额外开一个等长的数组;如果时间跨度很大而会议数量很少,改成把时间点排序后再扫描会更省内存。

用仓颉解决两道数组题:计数与最少会议室
https://www.violet27chen.com/posts/indexjiejue-in-cangjie/
作者
violet
发布于
2026-10-02
许可协议
CC BY 4.0

评论

评论将由 AI 自动审核;需要人工确认的内容会在审核通过后显示。

正在加载评论……