【LeetCode】117. 填充每个节点的下一个右侧节点指针 II

117. 填充每个节点的下一个右侧节点指针 II

文章目录

题目描述

给定一个二叉树:

C++ 复制代码
struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

示例 1:

输入root = [1,2,3,4,5,null,7]

输出[1,#,2,3,#,4,5,7,#]

解释 :给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

示例 2:

输入root = []

输出[]

提示:

  • 树中的节点数在范围 [0, 6000]
  • -100 <= Node.val <= 100

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。

问题深度分析

这是116题的扩展版本 ,核心区别在于普通二叉树 (节点可能缺失)与完美二叉树 (所有节点都存在)的处理差异。本题要求填充每个节点的 next 指针,使其指向同一层的下一个右侧节点,但树的结构不再保证完美,需要处理节点缺失的情况。

问题本质

给定一个普通二叉树,需要为每个节点建立指向同一层右侧相邻节点next 指针。关键挑战:

  • 节点可能缺失:左子树或右子树可能为空
  • 层结构不规则:每层节点数不确定
  • 跨子树连接复杂:需要找到下一个存在的节点

核心思想

处理节点缺失的情况

  1. BFS方法:逐层处理,跳过空节点
  2. 递归方法:需要处理子树为空的情况
  3. 迭代O(1)方法:需要找到每层第一个非空节点,处理节点缺失
  4. 虚拟头节点:使用dummy节点简化连接逻辑

关键难点

  1. 找到每层第一个节点:普通二叉树中,每层第一个节点位置不确定
  2. 处理节点缺失:左子树或右子树为空时,连接逻辑需要调整
  3. 跨子树连接:需要跳过空节点找到下一个存在的节点
  4. 空间优化:在O(1)空间内处理不规则的树结构

算法对比

方法 时间复杂度 空间复杂度 特点
方法一:BFS层序遍历 O(n) O(w) 直观易懂,需要队列存储
方法二:递归连接 O(n) O(h) 利用递归栈,需要处理空节点
方法三:迭代 O(1) 空间(虚拟头节点) O(n) O(1) 最优解,使用dummy简化逻辑
方法四:迭代 O(1) 空间(直接处理) O(n) O(1) 不使用dummy,直接处理

说明

  • n:节点总数
  • w:树的最大宽度(最后一层节点数)
  • h:树的高度

算法流程图

主算法流程













开始
根节点是否为空?
返回空
选择算法方法
方法一: BFS层序遍历
方法二: 递归连接
方法三: 迭代O1虚拟头节点
方法四: 迭代O1直接处理
创建队列
逐层处理节点
跳过空节点
连接同层相邻节点
处理下一层
是否还有层?
完成
处理当前节点
左子树存在?
连接左子树
跳过
右子树存在?
连接右子树
递归处理
利用父节点next找下一个
创建虚拟头节点dummy
从根节点开始逐层处理
使用dummy连接下一层
遍历当前层建立连接
移动到下一层
是否还有层?
找到每层第一个节点
遍历当前层
处理每个节点的子节点
找到下一个存在的节点
移动到下一层
是否还有层?
结束

方法三:虚拟头节点详细流程









开始: root节点
创建虚拟头节点dummy
初始化: curr = root
curr是否为空?
结束
初始化dummy.Next = nil
初始化tail = dummy
curr是否为空?
curr移动到下一层第一个节点
curr.Left存在?
tail.Next = curr.Left, tail = tail.Next
跳过
curr.Right存在?
tail.Next = curr.Right, tail = tail.Next
curr = curr.Next
curr = dummy.Next
返回root

节点缺失处理示意图

子节点层(有缺失)
父节点层
next
next
next
next
跳过
建立连接
建立连接
通过A.next建立
Node A
Node B
A.left
A.right
B.left
B.right
A.right缺失


复杂度分析

时间复杂度

所有方法均为 O(n)

  • 每个节点被访问一次
  • 每个节点的 next 指针被设置一次
  • 总操作次数与节点数成正比

空间复杂度

  1. 方法一(BFS):O(w)

    • 队列最多存储一层的节点
    • 普通二叉树最后一层节点数不确定
    • 最坏情况 w = O(n)
  2. 方法二(递归):O(h)

    • 递归调用栈深度等于树的高度
    • 普通二叉树高度 h 可能达到 O(n)
    • 最坏情况 O(n)
  3. 方法三(迭代 O(1) + dummy):O(1)

    • 只使用常数额外变量(dummy节点)
    • dummy节点不算额外空间(题目允许)
  4. 方法四(迭代 O(1) 直接):O(1)

    • 只使用常数额外变量
    • 需要找到每层第一个节点

关键优化技巧

技巧一:虚拟头节点(Dummy Node)

核心思想:使用虚拟头节点简化连接逻辑,避免处理每层第一个节点的特殊情况。

go 复制代码
// 方法三:迭代 O(1) 空间(虚拟头节点)
func connect3(root *Node) *Node {
    if root == nil {
        return nil
    }
    
    curr := root
    for curr != nil {
        // 创建虚拟头节点,用于连接下一层
        dummy := &Node{}
        tail := dummy
        
        // 遍历当前层,连接下一层节点
        for curr != nil {
            if curr.Left != nil {
                tail.Next = curr.Left
                tail = tail.Next
            }
            if curr.Right != nil {
                tail.Next = curr.Right
                tail = tail.Next
            }
            curr = curr.Next
        }
        
        // 移动到下一层
        curr = dummy.Next
    }
    
    return root
}

优势

  • 代码简洁,逻辑清晰
  • 统一处理所有节点,无需特殊判断
  • 空间复杂度 O(1)

技巧二:找到每层第一个节点

核心思想:在普通二叉树中,需要找到每层第一个非空节点作为起始点。

go 复制代码
// 方法四:迭代 O(1) 空间(直接处理)
func connect4(root *Node) *Node {
    if root == nil {
        return nil
    }
    
    curr := root
    
    // 逐层处理
    for curr != nil {
        // 找到当前层第一个有子节点的节点
        var nextLevelFirst *Node
        var nextLevelLast *Node
        
        // 找到下一层第一个节点
        for curr != nil {
            if curr.Left != nil {
                if nextLevelFirst == nil {
                    nextLevelFirst = curr.Left
                    nextLevelLast = curr.Left
                } else {
                    nextLevelLast.Next = curr.Left
                    nextLevelLast = nextLevelLast.Next
                }
            }
            if curr.Right != nil {
                if nextLevelFirst == nil {
                    nextLevelFirst = curr.Right
                    nextLevelLast = curr.Right
                } else {
                    nextLevelLast.Next = curr.Right
                    nextLevelLast = nextLevelLast.Next
                }
            }
            curr = curr.Next
        }
        
        curr = nextLevelFirst
    }
    
    return root
}

优势

  • 不使用dummy节点
  • 直接处理,逻辑直观
  • 空间复杂度 O(1)

技巧三:BFS 层序遍历

核心思想:使用队列逐层处理,跳过空节点,为每层节点建立连接。

go 复制代码
// 方法一:BFS层序遍历
func connect1(root *Node) *Node {
    if root == nil {
        return nil
    }
    
    queue := []*Node{root}
    for len(queue) > 0 {
        size := len(queue)
        var prev *Node
        
        for i := 0; i < size; i++ {
            node := queue[0]
            queue = queue[1:]
            
            // 连接同层相邻节点
            if prev != nil {
                prev.Next = node
            }
            prev = node
            
            // 添加子节点到队列(跳过空节点)
            if node.Left != nil {
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                queue = append(queue, node.Right)
            }
        }
    }
    
    return root
}

优势

  • 思路直观
  • 适用于任意二叉树
  • 易于理解和实现

技巧四:递归处理空节点

核心思想:递归处理时,需要处理子树为空的情况,利用父节点的next找到下一个节点。

go 复制代码
// 方法二:递归连接
func connect2(root *Node) *Node {
    if root == nil {
        return nil
    }
    
    // 找到当前节点右侧第一个有子节点的节点
    var next *Node
    p := root.Next
    for p != nil {
        if p.Left != nil {
            next = p.Left
            break
        }
        if p.Right != nil {
            next = p.Right
            break
        }
        p = p.Next
    }
    
    // 连接当前节点的子节点
    if root.Right != nil {
        root.Right.Next = next
        connect2(root.Right)
    }
    if root.Left != nil {
        if root.Right != nil {
            root.Left.Next = root.Right
        } else {
            root.Left.Next = next
        }
        connect2(root.Left)
    }
    
    return root
}

优势

  • 代码简洁
  • 逻辑清晰
  • 空间复杂度 O(h)

边界情况处理

1. 空树

go 复制代码
if root == nil {
    return nil
}

2. 单节点树

  • 根节点的 next 保持为 NULL
  • 无需特殊处理

3. 每层最后一个节点

  • 最后一个节点的 next 应为 NULL
  • BFS 方法中通过 prev 指针处理

4. 节点缺失情况

  • 左子树缺失:只处理右子树
  • 右子树缺失:只处理左子树
  • 左右子树都缺失:跳过,继续处理下一个节点

5. 层中所有节点都缺失子节点

  • 该层为叶子节点层
  • 迭代方法中 dummy.NextnextLevelFirstnil,循环结束

测试用例设计

基础测试

  1. 空树

    • 输入:[]
    • 输出:[]
  2. 单节点

    • 输入:[1]
    • 输出:[1,#]
  3. 两层树(完整)

    • 输入:[1,2,3]
    • 输出:[1,#,2,3,#]
  4. 两层树(左子树缺失)

    • 输入:[1,null,3]
    • 输出:[1,#,3,#]

标准测试

  1. 三层树(节点缺失)

    • 输入:[1,2,3,4,5,null,7]
    • 输出:[1,#,2,3,#,4,5,7,#]
    • 验证:
      • 节点2的next指向节点3
      • 节点4的next指向节点5
      • 节点5的next指向节点7
  2. 左偏树

    • 输入:[1,2,null,3,null,4]
    • 验证:每层节点正确连接
  3. 右偏树

    • 输入:[1,null,2,null,3,null,4]
    • 验证:每层节点正确连接

边界测试

  1. 完全不平衡树

    • 测试极端不平衡情况
    • 验证空间复杂度
  2. 所有节点只有左子树

    • 输入:[1,2,null,3,null,4]
    • 验证连接正确性
  3. 所有节点只有右子树

    • 输入:[1,null,2,null,3,null,4]
    • 验证连接正确性

常见错误与陷阱

错误一:未处理节点缺失

错误代码

go 复制代码
// 错误:假设所有节点都存在
curr.Left.Next = curr.Right
curr.Right.Next = curr.Next.Left

正确做法

go 复制代码
// 正确:检查节点是否存在
if curr.Left != nil {
    if curr.Right != nil {
        curr.Left.Next = curr.Right
    } else {
        // 需要找到下一个节点
    }
}

错误二:未找到正确的下一个节点

错误代码

go 复制代码
// 错误:直接使用next的子节点,可能为空
if curr.Next != nil {
    curr.Right.Next = curr.Next.Left  // Left可能为nil
}

正确做法

go 复制代码
// 正确:找到下一个有子节点的节点
p := curr.Next
for p != nil {
    if p.Left != nil {
        curr.Right.Next = p.Left
        break
    }
    if p.Right != nil {
        curr.Right.Next = p.Right
        break
    }
    p = p.Next
}

错误三:未找到每层第一个节点

错误代码

go 复制代码
// 错误:假设每层第一个节点在固定位置
leftmost = leftmost.Left  // Left可能为nil

正确做法

go 复制代码
// 正确:找到下一层第一个非空节点
dummy := &Node{}
tail := dummy
// ... 连接下一层节点
curr = dummy.Next  // 下一层第一个节点

错误四:BFS中队列大小变化

错误代码

go 复制代码
for len(queue) > 0 {
    node := queue[0]
    queue = queue[1:]
    // 错误:队列大小在循环中变化
    if len(queue) > 0 {
        node.Next = queue[0]
    }
}

正确做法

go 复制代码
for len(queue) > 0 {
    size := len(queue)  // 固定当前层大小
    var prev *Node
    for i := 0; i < size; i++ {
        node := queue[0]
        queue = queue[1:]
        if prev != nil {
            prev.Next = node
        }
        prev = node
    }
}

实战技巧总结

1. 虚拟头节点的妙用

  • 简化逻辑:统一处理所有节点,无需特殊判断
  • 代码清晰:减少条件分支,提高可读性
  • 空间优化:dummy节点不算额外空间

2. 处理节点缺失的策略

  • 检查存在性:先检查节点是否存在再操作
  • 跳过空节点:遍历时跳过空节点
  • 找到下一个:需要找到下一个存在的节点

3. 空间优化思路

  • 利用next指针:使用已建立的next指针遍历
  • 逐层处理:处理完一层再处理下一层
  • 虚拟头节点:使用dummy简化连接逻辑

4. 与116题的区别

  • 116题(完美二叉树) :可以利用结构特性,直接访问 curr.Next.Left
  • 117题(普通二叉树):需要检查节点存在性,找到下一个存在的节点

进阶扩展

扩展一:N叉树版本

  • 每个节点有多个子节点
  • 需要连接所有相邻的子节点
  • 算法需要相应调整

扩展二:从任意节点开始

  • 给定树中某个节点
  • 填充该节点所在层的所有 next 指针
  • 需要先找到该层的起始节点

扩展三:双向链表

  • 不仅建立向右的 next 指针
  • 还建立向左的 prev 指针
  • 形成双向链表结构

扩展四:按Z字形连接

  • 奇数层从左到右连接
  • 偶数层从右到左连接
  • 需要调整连接顺序

应用场景

1. 树形结构的横向遍历

  • 场景:需要按层遍历树,但不想使用队列
  • 优势 :利用 next 指针可以直接遍历同一层

2. 普通二叉树的层序操作

  • 场景:对普通二叉树进行层序相关操作
  • 优势:处理节点缺失的情况

3. 空间受限环境

  • 场景:内存有限,需要 O(1) 空间算法
  • 优势:迭代方法满足空间要求

4. 树形数据结构的序列化

  • 场景:需要按层序列化树结构
  • 优势next 指针提供了层序信息

相关题目

  • 116. 填充每个节点的下一个右侧节点指针:完美二叉树版本
  • 102. 二叉树的层序遍历:层序遍历基础
  • 103. 二叉树的锯齿形层序遍历:层序遍历变种
  • 199. 二叉树的右视图:利用层序遍历

总结

本题是116题的扩展版本 ,核心在于处理普通二叉树中节点缺失的情况。通过四种不同的方法,我们展示了:

  1. BFS 方法:直观易懂,适用于理解问题
  2. 递归方法:代码简洁,需要处理空节点
  3. 迭代 O(1) + dummy:最优解,使用虚拟头节点简化逻辑
  4. 迭代 O(1) 直接:不使用dummy,直接处理

关键要点

  • 普通二叉树需要处理节点缺失的情况
  • 虚拟头节点可以大大简化连接逻辑
  • 需要找到每层第一个非空节点
  • 跨子树连接需要找到下一个存在的节点

掌握本题有助于理解普通二叉树的层序遍历空间优化技巧 ,特别是虚拟头节点这一重要技巧,在链表和树的问题中都有广泛应用。

完整题解代码

go 复制代码
package main

import (
	"fmt"
)

// =========================== Node 定义 ===========================
type Node struct {
	Val   int
	Left  *Node
	Right *Node
	Next  *Node
}

// =========================== 方法一:BFS层序遍历 ===========================
// 时间复杂度:O(n),空间复杂度:O(w),w为树的最大宽度
func connect1(root *Node) *Node {
	if root == nil {
		return nil
	}

	queue := []*Node{root}
	for len(queue) > 0 {
		size := len(queue)
		var prev *Node

		for i := 0; i < size; i++ {
			node := queue[0]
			queue = queue[1:]

			// 连接同层相邻节点
			if prev != nil {
				prev.Next = node
			}
			prev = node

			// 添加子节点到队列(跳过空节点)
			if node.Left != nil {
				queue = append(queue, node.Left)
			}
			if node.Right != nil {
				queue = append(queue, node.Right)
			}
		}
	}

	return root
}

// =========================== 方法二:递归连接 ===========================
// 时间复杂度:O(n),空间复杂度:O(h),h为树的高度
func connect2(root *Node) *Node {
	if root == nil {
		return nil
	}

	// 找到当前节点右侧第一个有子节点的节点
	var next *Node
	p := root.Next
	for p != nil {
		if p.Left != nil {
			next = p.Left
			break
		}
		if p.Right != nil {
			next = p.Right
			break
		}
		p = p.Next
	}

	// 连接当前节点的子节点(先处理右子树,再处理左子树)
	if root.Right != nil {
		root.Right.Next = next
		connect2(root.Right)
	}
	if root.Left != nil {
		if root.Right != nil {
			root.Left.Next = root.Right
		} else {
			root.Left.Next = next
		}
		connect2(root.Left)
	}

	return root
}

// =========================== 方法三:迭代 O(1) 空间(虚拟头节点) ===========================
// 时间复杂度:O(n),空间复杂度:O(1)
func connect3(root *Node) *Node {
	if root == nil {
		return nil
	}

	curr := root
	for curr != nil {
		// 创建虚拟头节点,用于连接下一层
		dummy := &Node{}
		tail := dummy

		// 遍历当前层,连接下一层节点
		for curr != nil {
			if curr.Left != nil {
				tail.Next = curr.Left
				tail = tail.Next
			}
			if curr.Right != nil {
				tail.Next = curr.Right
				tail = tail.Next
			}
			curr = curr.Next
		}

		// 移动到下一层
		curr = dummy.Next
	}

	return root
}

// =========================== 方法四:迭代 O(1) 空间(直接处理) ===========================
// 时间复杂度:O(n),空间复杂度:O(1)
func connect4(root *Node) *Node {
	if root == nil {
		return nil
	}

	curr := root

	// 逐层处理
	for curr != nil {
		// 找到下一层第一个节点和最后一个节点
		var nextLevelFirst *Node
		var nextLevelLast *Node

		// 遍历当前层,建立下一层的连接
		for curr != nil {
			if curr.Left != nil {
				if nextLevelFirst == nil {
					nextLevelFirst = curr.Left
					nextLevelLast = curr.Left
				} else {
					nextLevelLast.Next = curr.Left
					nextLevelLast = nextLevelLast.Next
				}
			}
			if curr.Right != nil {
				if nextLevelFirst == nil {
					nextLevelFirst = curr.Right
					nextLevelLast = curr.Right
				} else {
					nextLevelLast.Next = curr.Right
					nextLevelLast = nextLevelLast.Next
				}
			}
			curr = curr.Next
		}

		// 移动到下一层
		curr = nextLevelFirst
	}

	return root
}

// =========================== 工具函数:从数组构建二叉树 ===========================
func arrayToTreeLevelOrder(arr []interface{}) *Node {
	if len(arr) == 0 || arr[0] == nil {
		return nil
	}

	root := &Node{Val: arr[0].(int)}
	queue := []*Node{root}
	i := 1

	for i < len(arr) && len(queue) > 0 {
		node := queue[0]
		queue = queue[1:]

		// 左子节点
		if i < len(arr) && arr[i] != nil {
			left := &Node{Val: arr[i].(int)}
			node.Left = left
			queue = append(queue, left)
		}
		i++

		// 右子节点
		if i < len(arr) && arr[i] != nil {
			right := &Node{Val: arr[i].(int)}
			node.Right = right
			queue = append(queue, right)
		}
		i++
	}

	return root
}

// =========================== 工具函数:按层序输出(带next信息) ===========================
func levelOrderWithNext(root *Node) []interface{} {
	if root == nil {
		return []interface{}{}
	}

	var result []interface{}
	queue := []*Node{root}

	for len(queue) > 0 {
		size := len(queue)
		for i := 0; i < size; i++ {
			node := queue[0]
			queue = queue[1:]
			result = append(result, node.Val)

			// 通过next指针找到同层下一个节点
			if node.Next != nil {
				// 继续处理同层节点
			} else {
				// 这一层结束了
			}

			// 添加子节点(用于下一层遍历)
			if node.Left != nil {
				queue = append(queue, node.Left)
			}
			if node.Right != nil {
				queue = append(queue, node.Right)
			}
		}
		result = append(result, "#") // 层分隔符
	}

	return result
}

// =========================== 工具函数:通过next指针按层输出 ===========================
func levelOrderByNext(root *Node) []interface{} {
	if root == nil {
		return []interface{}{}
	}

	var result []interface{}
	curr := root

	// 找到每层第一个节点
	for curr != nil {
		// 找到当前层第一个节点
		levelStart := curr
		for levelStart != nil && levelStart.Left == nil && levelStart.Right == nil {
			levelStart = levelStart.Next
		}
		if levelStart == nil {
			break
		}

		// 通过next指针遍历当前层
		p := levelStart
		for p != nil {
			result = append(result, p.Val)
			p = p.Next
		}
		result = append(result, "#")

		// 找到下一层第一个节点
		nextLevelStart := (*Node)(nil)
		p = levelStart
		for p != nil {
			if p.Left != nil {
				nextLevelStart = p.Left
				break
			}
			if p.Right != nil {
				nextLevelStart = p.Right
				break
			}
			p = p.Next
		}
		curr = nextLevelStart
	}

	return result
}

// =========================== 工具函数:验证next指针连接 ===========================
func validateNextPointers(root *Node) []interface{} {
	if root == nil {
		return []interface{}{}
	}

	var result []interface{}
	queue := []*Node{root}

	for len(queue) > 0 {
		size := len(queue)
		for i := 0; i < size; i++ {
			node := queue[0]
			queue = queue[1:]
			result = append(result, node.Val)

			// 添加子节点到队列
			if node.Left != nil {
				queue = append(queue, node.Left)
			}
			if node.Right != nil {
				queue = append(queue, node.Right)
			}
		}
		result = append(result, "#") // 层分隔符
	}

	return result
}

// =========================== 工具函数:比较结果 ===========================
func equal(a, b []interface{}) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// =========================== 测试 ===========================
func main() {
	fmt.Println("=== LeetCode 117: 填充每个节点的下一个右侧节点指针 II ===\n")

	testCases := []struct {
		name     string
		root     *Node
		expected []interface{}
	}{
		{
			name:     "空树",
			root:     arrayToTreeLevelOrder([]interface{}{}),
			expected: []interface{}{},
		},
		{
			name:     "单节点",
			root:     arrayToTreeLevelOrder([]interface{}{1}),
			expected: []interface{}{1, "#"},
		},
		{
			name:     "两层树(完整)",
			root:     arrayToTreeLevelOrder([]interface{}{1, 2, 3}),
			expected: []interface{}{1, "#", 2, 3, "#"},
		},
		{
			name:     "两层树(左子树缺失)",
			root:     arrayToTreeLevelOrder([]interface{}{1, nil, 3}),
			expected: []interface{}{1, "#", 3, "#"},
		},
		{
			name:     "三层树(节点缺失)",
			root:     arrayToTreeLevelOrder([]interface{}{1, 2, 3, 4, 5, nil, 7}),
			expected: []interface{}{1, "#", 2, 3, "#", 4, 5, 7, "#"},
		},
		{
			name:     "左偏树",
			root:     arrayToTreeLevelOrder([]interface{}{1, 2, nil, 3, nil, 4}),
			expected: []interface{}{1, "#", 2, "#", 3, "#", 4, "#"},
		},
		{
			name:     "右偏树",
			root:     arrayToTreeLevelOrder([]interface{}{1, nil, 2, nil, 3, nil, 4}),
			expected: []interface{}{1, "#", 2, "#", 3, "#", 4, "#"},
		},
	}

	methods := []struct {
		name string
		fn   func(*Node) *Node
	}{
		{"方法一:BFS层序遍历", connect1},
		{"方法二:递归连接", connect2},
		{"方法三:迭代O(1)空间(虚拟头节点)", connect3},
		{"方法四:迭代O(1)空间(直接处理)", connect4},
	}

	allPassed := true
	for _, method := range methods {
		fmt.Printf("--- %s ---\n", method.name)
		passed := 0
		for i, tc := range testCases {
			// 重新构建树(因为会修改原树)
			testRoot := arrayToTreeLevelOrder(getTreeArray(tc.name))
			result := method.fn(testRoot)
			got := validateNextPointers(result)

			if equal(got, tc.expected) {
				fmt.Printf("  Test %d: ✓ PASSED\n", i+1)
				passed++
			} else {
				fmt.Printf("  Test %d: ✗ FAILED\n", i+1)
				fmt.Printf("    输入: %v\n", getTreeArray(tc.name))
				fmt.Printf("    期望: %v\n", tc.expected)
				fmt.Printf("    得到: %v\n", got)
				allPassed = false
			}
		}
		fmt.Printf("通过率: %d/%d\n\n", passed, len(testCases))
	}

	if allPassed {
		fmt.Println("🎉 所有测试通过!")
	} else {
		fmt.Println("❌ 部分测试失败")
	}
}

// 辅助函数:根据测试用例名称获取树数组
func getTreeArray(name string) []interface{} {
	switch name {
	case "空树":
		return []interface{}{}
	case "单节点":
		return []interface{}{1}
	case "两层树(完整)":
		return []interface{}{1, 2, 3}
	case "两层树(左子树缺失)":
		return []interface{}{1, nil, 3}
	case "三层树(节点缺失)":
		return []interface{}{1, 2, 3, 4, 5, nil, 7}
	case "左偏树":
		return []interface{}{1, 2, nil, 3, nil, 4}
	case "右偏树":
		return []interface{}{1, nil, 2, nil, 3, nil, 4}
	default:
		return []interface{}{}
	}
}
相关推荐
yujunl1 小时前
排除一个版本原因导致Mybatis Plus不能分页的问题
java
上海合宙LuatOS1 小时前
LuatOS核心库API——【fatfs】支持FAT32文件系统
java·前端·网络·数据库·单片机·嵌入式硬件·物联网
晓13132 小时前
第五章 【若依框架:优化】高级特性与性能优化
java·开发语言·性能优化·若依
一叶之秋14122 小时前
基石之力:掌握 C++ 继承的核心奥秘
开发语言·c++·算法
大模型玩家七七2 小时前
效果评估:如何判断一个祝福 AI 是否“走心”
android·java·开发语言·网络·人工智能·batch
拾光Ծ2 小时前
【优选算法】滑动窗口算法:专题一
c++·算法·滑动窗口·c++算法·滑动窗口算法·笔试面试
河码匠2 小时前
设计模式之依赖注入(Dependency Injection)
java·设计模式·log4j
im_AMBER2 小时前
Leetcode 118 从中序与后序遍历序列构造二叉树 | 二叉树的最大深度
数据结构·学习·算法·leetcode
YuTaoShao2 小时前
【LeetCode 每日一题】3721. 最长平衡子数组 II ——(解法二)分块
java·算法·leetcode