如果多个背包快照共享一棵只读树,修改一个格子时可以复制从根到该叶子的路径,其余子树继续共用。这样旧快照仍然保留原值,而不必每次复制整个背包。
先明确输入与规则
本例八个格子组成满二叉树,叶子存数值,内部节点只存左右孩子引用。修改一个格子时创建新叶子,并沿路径创建新父节点;未经过的分支引用原节点。若新值等于原值,整条路径都复用,返回原根。每次操作后保存根作为一个快照。
本例的独立记录
最初八格[5, 4, 19, 4, 13, 2, 18, 18],初始树十五节点。下面显示每次操作后保存的新根与逻辑内容。
格号、新值 | 新根编号 | 本次新建节点 | 已创建总节点 | 新快照内容 [6, 18] | 14 | 0 | 15 | [5, 4, 19, 4, 13, 2, 18, 18] [0, 4] | 18 | 4 | 19 | [4, 4, 19, 4, 13, 2, 18, 18] [2, 4] | 22 | 4 | 23 | [4, 4, 4, 4, 13, 2, 18, 18] [5, 2] | 22 | 0 | 23 | [4, 4, 4, 4, 13, 2, 18, 18] [3, 19] | 26 | 4 | 27 | [4, 4, 4, 19, 13, 2, 18, 18] [5, 15] | 30 | 4 | 31 | [4, 4, 4, 19, 13, 15, 18, 18] [4, 13] | 30 | 0 | 31 | [4, 4, 4, 19, 13, 15, 18, 18] [2, 11] | 34 | 4 | 35 | [4, 4, 11, 19, 13, 15, 18, 18] 全部历史根编号:[14, 14, 18, 22, 22, 26, 30, 30, 34]
记录中的编号只用来区分本例对象,不应与大小排名或另一组记录的身份混淆。先核对单位和边界,再跟随中间值检查结果。空结果、零值与尚未确定是不同状态;分数保留精确含义,显示小数只为方便阅读。
怎样复算
从任意旧根读出的八个值必须保持原样。真正改变值时,这棵八叶树需要新建一个叶子和三个祖先,共四个节点;无变化则新建零个。不同快照共享节点是预期行为,只要共享节点不再被直接改写。
验证时分别比较逻辑内容和物理节点身份:内容相同不代表使用同一个节点,节点复用也不代表两个完整快照完全一样。把新根保存好才得到新版本;若覆盖共享叶子的值,会同时改坏多个历史快照,失去这种结构最重要的性质。
适用条件与限制
这里所有历史根都保留,也没有并发写入、磁盘格式或自动回收。共享结构减少复制量,但历史保留越久仍会增加占用;如果丢弃旧根,需要按可达性规则另行回收,不能直接删除仍被其他快照引用的节点。
练习时可以一次只改变一个条件,比较差异从哪个步骤开始。若自己的结果不同,先核对是否采用同样的规则,再定位第一个不一致的中间状态,不要只修改最终答案。输入与运算过程需要一起保存,才能判断结论究竟适用于哪一个具体场景。
