【LeetCode】427. 建立四叉树

leetcode

 

解题思路

构建四叉树的核心是​​分治策略​​:将矩阵递归划分为四个子区域(左上、右上、左下、右下),直到子区域内所有值相同(全0或全1)或无法再划分。每个节点需根据当前区域的值一致性决定是否为叶子节点:

  • ​​叶子节点​​:区域值全相同,isLeaf=trueval=区域值,子节点为nil
  • ​​非叶子节点​​:区域值不同,isLeaf=falseval可任意,继续递归划分四个子区域。

关键步骤

  1. ​​递归函数设计​​

    • 参数:当前子矩阵的起始行r0、起始列c0、边长n
    • 终止条件:当前子矩阵所有值相同 → 返回叶子节点。
    • 递归划分:若值不同,将矩阵分为4个子矩阵(边长n/2),递归构建子节点。
  2. ​​值一致性检查​​

    • 遍历当前子矩阵,若存在不同值则返回false,否则返回true并记录一致值。
  3. ​​子区域划分​​

    • ​​左上​​:(r0, c0, n/2)
    • ​​右上​​:(r0, c0 + n/2, n/2)
    • ​​左下​​:(r0 + n/2, c0, n/2)
    • ​​右下​​:(r0 + n/2, c0 + n/2, n/2)
  4. ​​节点创建​​

    • 叶子节点:isLeaf=trueval=一致值, 子节点为nil
    • 非叶子节点:isLeaf=falseval可任意(如true),子节点为递归结果。

代码实现

type Node struct {
    Val         bool
    IsLeaf      bool
    TopLeft     *Node
    TopRight    *Node
    BottomLeft  *Node
    BottomRight *Node
}

func construct(grid [][]int) *Node {
    n := len(grid)
    return build(0, 0, n, grid)
}

// 递归构建四叉树:r0/c0=起始行列,size=当前区域边长
func build(r0, c0, size int, grid [][]int) *Node {
    // 检查当前区域是否全相同
    allSame, val := checkUniform(r0, c0, size, grid)
    if allSame {
        return &Node{Val: val, IsLeaf: true} // 叶子节点
    }

    // 非叶子节点:划分四个子区域
    half := size / 2
    return &Node{
        IsLeaf:      false,
        Val:         true,                                // 任意值,判题机制接受
        TopLeft:     build(r0, c0, half, grid),           // 左上
        TopRight:    build(r0, c0+half, half, grid),      // 右上
        BottomLeft:  build(r0+half, c0, half, grid),      // 左下
        BottomRight: build(r0+half, c0+half, half, grid), // 右下
    }
}

// 检查区域值是否一致
func checkUniform(r0, c0, size int, grid [][]int) (bool, bool) {
    base := grid[r0][c0]
    for i := r0; i < r0+size; i++ {
        for j := c0; j < c0+size; j++ {
            if grid[i][j] != base {
                return false, false // 存在不同值
            }
        }
    }
    return true, base == 1 // 全部相同,返回true和值(true对应1)
}

示例测试

// 打印四叉树, 测试使用
func printTree(root *Node) {
    if root == nil {
        fmt.Println("[]")
        return
    }
    queue := []*Node{root}
    var res [][]int

    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        if node == nil {
            res = append(res, nil)
        } else {
            res = append(res, []int{boolToInt(node.IsLeaf), boolToInt(node.Val)})
            queue = append(queue, node.TopLeft, node.TopRight, node.BottomLeft, node.BottomRight)
        }
    }

    // 移除末尾 nil
    i := len(res) - 1
    for i >= 0 && res[i] == nil {
        i--
    }
    fmt.Println(res[:i+1])
}

func boolToInt(b bool) int {
    if b {
        return 1
    }
    return 0
}

func main() {
    // 示例1:2x2矩阵
    grid1 := [][]int{{0, 1}, {1, 0}}
    root1 := construct(grid1)
    printTree(root1) // 输出序列化格式(需自行实现层序遍历)

    // 示例2:8x8矩阵
    grid2 := [][]int{
        {1, 1, 1, 1, 0, 0, 0, 0},
        {1, 1, 1, 1, 0, 0, 0, 0},
        {1, 1, 1, 1, 1, 1, 1, 1},
        {1, 1, 1, 1, 1, 1, 1, 1},
        {1, 1, 1, 1, 0, 0, 0, 0},
        {1, 1, 1, 1, 0, 0, 0, 0},
        {1, 1, 1, 1, 0, 0, 0, 0},
        {1, 1, 1, 1, 0, 0, 0, 0},
    }
    root2 := construct(grid2)
    printTree(root2)

    // 边界测试:全1矩阵
    grid3 := [][]int{{1, 1}, {1, 1}}
    root3 := construct(grid3) // 应返回单个叶子节点 [1,1]
    printTree(root3)
}

复杂度分析

​​维度​​​​结果​​​​说明​​
​​时间复杂度​​ ​​O(n² log n)​​ 每次递归需检查O(n²)元素(最坏情况),递归深度O(log n)(每次边长减半)。
​​空间复杂度​​ ​​O(n²)​​ 存储所有节点(最坏有O(n²)叶子节点)。
​​适用场景​​ n ≤ 64 (2⁶) 题目约束n=2^x (0≤x≤6),递归深度最多6层。

关键点总结

  1. ​​分治递归​​:核心是​​自顶向下划分区域​​,通过build函数递归处理子网格。
  2. ​​终止条件​​:区域值全相同 → ​​立即返回叶子节点​​,避免无效划分。
  3. ​​节点类型​​:
    • ​​叶子节点​​:isLeaf=trueval为区域值,子节点为nil
    • ​​非叶子节点​​:isLeaf=falseval可任意(按题目要求设true),子节点必不为nil
  4. ​​坐标计算​​:子区域划分需精确计算​​中间行列​​(r0 + size/2c0 + size/2)。
  5. ​​优化方向​​:
    • 值检查优化:若首元素与后续不同可提前终止遍历。
    • 大矩阵处理:并行构建四个子节点(Go的goroutine+sync.WaitGroup)。
posted @ 2025-06-24 15:03  云隙之间  阅读(44)  评论(0)    收藏  举报