两道题的迁移记录:统计一串数字里每个数字出现的次数,以及给定若干会议区间求最少需要几间会议室。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) + 1for j in sorted(count.keys()): print(f"{j}:{count[j]}")仓颉的字典我还没用熟,于是换成数组下标当“桶”:数字本身就是下标,槽位里放出现次数。先在 Python 里把思路验证一遍:
# 列表解决统计数字问题num = input()count = [0] * 10for i in num: count[int(i)] += 1for j in range(10): if count[j] == 0: continue else: print(f"{j}:{count[j]}")两种写法输出一致:
迁移到仓颉时卡了一下:它不会把字符自动转成 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]}") }}运行结果:
顺带一提,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))运行结果:
问题在于仓颉的数组既没有 remove 也没有 append,这条路走不通。于是改用差分数组:先扫一遍拿到最晚的结束时间,建一个长度为「最晚结束时间 + 1」的数组,在每个会议的开始位置 +1、结束位置 −1,最后从头到尾累加,累加过程中的峰值就是同时进行的会议数上限,也就是最少需要的会议室数。Python 验证:
meeting_times = [(0, 600), (200, 700), (650, 1200)]last_end_time = 0for 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] -= 1occupied = 0rooms = 0for change in changes: occupied += change rooms = max(rooms, occupied)print(f"最少需要的会议室: {rooms}")运行结果:
同样思路的仓颉版:
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}")}运行结果:
我在实现时卡住的地方
仓颉里字符为什么不能直接转 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] 会越界。
差分数组适合什么场景?
适合数值范围已知、只对静态数据做区间加减、最后统一求前缀和的场景。代价是要先知道取值范围的上界(这里取最晚结束时间),并额外开一个等长的数组;如果时间跨度很大而会议数量很少,改成把时间点排序后再扫描会更省内存。
如果内容对你有帮助,欢迎用微信或支付宝请我喝杯咖啡 ☕


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