Python Trie迭代逻辑疑问:cur_node跨迭代状态异常原因
字典树代码执行逻辑疑问解答
核心前置认知
- Python中对字典这类可变对象的赋值,不会生成一份全新的拷贝,只是给同一个内存地址的对象绑定一个新的变量名,两个变量指向完全相同的对象,修改任意一个变量对应的内容,另一个变量读取到的内容会同步变化。
- 代码里的
cur_node本质是一个动态切换指向的“引用标签”,不是对某层字典的独立拷贝。
逐轮执行流拆解,对应疑问点定位
第一轮遍历:处理字符串'ab'
把每一步的实际指向和对象内容逐行对应原代码输出:
- 外层循环启动,执行
cur_node = Trie:此时根字典Trie是空字典{},cur_node和Trie完全指向同一个空对象,没有任何内容复制。 - 进入内层循环,遍历
i[:-1](即'ab'去掉最后一位的字符'a'):- 判断
'a'不在当前cur_node(也就是根字典Trie)中,执行cur_node[x] = {}:直接在根字典里新增a键,对应值为一个空字典。此时打印的1 {'a': {}}就是根字典的完整内容,因为此刻cur_node还停留在根层级。 - 打印
pre阶段,cur_node仍然指向根字典,所以输出{'a': {}},和上一步输出一致。 - 执行
cur_node = cur_node[x]:将cur_node的指向从根字典,挪到根字典里a键对应的那个空字典上。此时打印的2 {},就是这个a对应的子字典的内容——这一刻cur_node已经和根Trie不再是同一个对象了。
- 判断
- 内层循环因为
'ab'去掉最后一位只有1个元素,直接退出。执行末尾的cur_node[i[-1]] = '$':给当前cur_node指向的a对应的子字典,新增b键,值为'$'。- 这时候打印的
last {'b': '$'},就是这个a对应的子字典的独立内容。重点:此时根字典Trie的实际内容已经是{'a': {'b': '$'}}了,只是因为cur_node的标签已经挪到子字典层级,没有打印根层的内容而已。
- 这时候打印的
第二轮遍历:处理字符串'ac'
对应标注的核心疑问点:
- 外层循环启动,第一行就执行
cur_node = Trie——这一步直接把cur_node的标签,从上次结束时停留的子字典位置,重新贴回根字典对象上。 - 进入内层循环,遍历
'ac'去掉最后一位的字符'a':- 判断
'a'是否在当前cur_node(也就是重新指回的根字典)中:根字典从第一轮开始就存着a键,对应的值就是那个已经存了b:'$'的子字典,所以完全不会走if x not in cur_node的新建分支。 - 这时候打印
pre,输出{'a': {'b': '$'}}——就是根字典的本来面目。疑惑的“a键从哪里来”,答案很简单:这个a键在第一轮第一次修改根字典的时候就已经存在了,只是第一轮最后cur_node跑到了子字典层级,没打印根层内容,让人误以为根字典的内容丢了。 - 执行
cur_node = cur_node[x],把cur_node的指向重新挪到a对应的子字典上,所以打印的2 {'b': '$'}就是这个子字典的现有内容,和第一轮最后看到的完全一致。
- 判断
- 内层循环退出,给当前cur_node指向的
a子字典新增c键,值为'$',就形成了最终的子结构{'b':'$', 'c':'$'},最后打印整个Trie就是完整的字典树结构。
认知误区澄清
- 之前的误解本质是:以为第一轮循环结束后,根字典Trie的内容被重置、或者cur_node是一个独立的字典副本。但实际上:
- 全程没有任何字典内容的拷贝操作,cur_node只是不断切换指向的引用标签。
- 第一轮最后看到的
{'b':'$'}是cur_node指向的子层级字典,不是根字典本身,根字典从第一轮加完a键开始就一直保持着原有结构,从来没有被清空过。
- 整个过程和浅拷贝特性无关,因为代码里根本没有执行过任何拷贝操作,全程都是对原字典对象的直接修改、以及引用指向的动态调整。
内容的提问来源于stack exchange,提问作者xiaoming Li
相关产品推荐
相关产品推荐

