Go中树节点必须用*TreeNode而非TreeNode定义,否则编译失败;初始化需取地址,访问前必判nil;深拷贝须手动递归实现。

树节点定义必须显式包含指针类型字段
Go 没有隐式引用,struct 里存子节点必须用 *TreeNode 而不是 TreeNode,否则递归嵌套会编译失败或无限展开。
- 错误写法:
type TreeNode struct { Left TreeNode; Right TreeNode }→ 编译报错invalid recursive type TreeNode - 正确写法:
type TreeNode struct { Left *TreeNode; Right *TreeNode; Val int } - 如果漏掉
*,Go 会尝试把整个结构体复制进自身,直接拒绝编译 - 即使只存一个子节点(比如单链式树),字段类型也得是
*TreeNode,不然无法表示“空”
初始化树节点时别忘了取地址或用 & 或 new()
声明变量后不取地址,*TreeNode 字段默认是 nil;但想让它指向新节点,得明确分配内存并传地址。
-
root := TreeNode{Val: 1}→root.Left是nil,不能直接赋值子节点 - 要让
root.Left指向新节点,得:root.Left = &TreeNode{Val: 2}或root.Left = new(TreeNode); root.Left.Val = 2 - 常见坑:写
root.Left = TreeNode{Val: 2}→ 类型不匹配(TreeNode≠*TreeNode) - 构造深层树时,嵌套字面量里也得用
&,比如:&TreeNode{Val: 1, Left: &TreeNode{Val: 2}}
遍历/修改树时 nil 检查比想象中更频繁
Go 不做空指针自动防护,任何通过 *TreeNode 访问字段或方法前,都得先确认它非 nil,否则 panic 报 invalid memory address or nil pointer dereference。
- 递归函数开头几乎都要加:
if node == nil { return }或if node == nil { return 0 } - 不能假设父节点存在就代表子节点存在 ——
node.Left和node.Right默认就是nil - 插入逻辑里容易漏判:
if node.Left == nil { node.Left = &TreeNode{Val: v} },少这行就会静默失败 - 用
switch处理多分支时,case node.Left != nil:比case node.Left:更安全(后者语法错误)
深拷贝树必须手动递归,不可用 reflect.DeepCopy 或 json.Marshal/Unmarshal
Go 标准库没有通用深拷贝函数,而 json.Marshal + json.Unmarshal 会丢字段标签、破坏未导出字段、且性能差;reflect 包操作复杂易错。
立即学习“go语言免费学习笔记(深入)”;
- 最稳方式是写一个显式递归函数:
func copyTree(root *TreeNode) *TreeNode - 函数内对每个非
nil子节点调用自身,并赋给新节点的对应字段 - 错误示例:
newRoot := *root→ 浅拷贝,newRoot.Left和root.Left指向同一内存 - 如果树含自定义字段(如
parent *TreeNode),还要决定是否复制反向指针,这点常被忽略










