当前位置:首页 > 技术 > 正文内容

二叉树算法题解集

访客 技术 2026年6月12日 1

二叉树节点计数

给定一棵完全二叉树的根节点,计算该树中节点的总数。

方法一:广度优先搜索

通过层次遍历方式,在访问节点过程中计数。

public int countNodes(TreeNode root) {
    if (root == null) return 0;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    int nodeCount = 0;
    while (!queue.isEmpty()) {
        nodeCount++;
        TreeNode currentNode = queue.poll();
        if (currentNode.left != null) queue.offer(currentNode.left);
        if (currentNode.right != null) queue.offer(currentNode.right);
    }
    return nodeCount;
}

方法二:递归法

递归思路:树的节点数等于左子树节点数加右子树节点数加1,递归终止条件是节点为空。

简化递归实现:

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

详细递归实现:

class Solution {
    public int countNodes(TreeNode root) {
        return calculateNodeCount(root);
    }

    /**
     * 计算指定节点所在子树的节点总数
     * @param node 当前节点
     * @return 子树节点总数
     */
    public int calculateNodeCount(TreeNode node) {
        if (node == null) return 0;
        int leftCount = calculateNodeCount(node.left);
        int rightCount = calculateNodeCount(node.right);
        return leftCount + rightCount + 1;
    }
}

方法三:优化递归法

利用完全二叉树的特性优化:

  1. 左右子树都是满二叉树,此时节点数为2^h-1
  2. 左子树为满二叉树,右子树为普通完全二叉树
  3. 左右子树都不是满二叉树

代码实现:

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        TreeNode leftChild = root.left;
        TreeNode rightChild = root.right;
        int leftDepth = 0, rightDepth = 0;
        while (leftChild != null) {
            leftDepth++;
            leftChild = leftChild.left;
        }
        while (rightChild != null) {
            rightDepth++;
            rightChild = rightChild.right;
        }
        if (leftDepth == rightDepth) {
            return (2 << leftDepth) - 1;
        }
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

平衡二叉树判断

判断给定的二叉树是否为平衡二叉树。

解题思路:递归获取左右子树的高度,当发现不平衡时,返回-1标识非平衡状态。

class Solution {
    public boolean isBalanced(TreeNode root) {
        return getTreeHeight(root) != -1;
    }

    int getTreeHeight(TreeNode node) {
        if (node == null) return 0;
        int leftHeight = getTreeHeight(node.left);
        int rightHeight = getTreeHeight(node.right);
        if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1){
            return -1;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

二叉树路径问题

从根到叶的所有路径

给定二叉树的根节点,返回所有从根节点到叶子节点的路径。

方法一:递归回溯

List<String> pathResults = new ArrayList<>();
List<String> currentPath = new ArrayList<>();

public List<String> getAllPaths(TreeNode root) {
    collectPaths(root);
    return pathResults;
}

void collectPaths(TreeNode node) {
    if (node.left == null && node.right == null) {
        currentPath.add(node.val + "");
        addPathToList(currentPath);
        currentPath.remove(currentPath.size() - 1);
        return;
    }
    currentPath.add(node.val + "->");
    if (node.left != null) collectPaths(node.left);
    if (node.right != null) collectPaths(node.right);
    currentPath.remove(currentPath.size() - 1);
}

public void addPathToList(List path) {
    String pathStr = "";
    for (int i = 0; i < path.size(); i++) {
        pathStr += path.get(i);
    }
    pathResults.add(pathStr);
}

方法二:迭代法

class Solution {
    public List<String> getAllPaths(TreeNode root) {
        Stack<Object> stack = new Stack<>();
        List<String> results = new ArrayList<>();
        if (root != null) {
            stack.push(root);
            stack.push(root.val + "");
        }
        while (!stack.isEmpty()) {
            String currentPath = (String) stack.pop();
            TreeNode currentNode = (TreeNode) stack.pop();
            if (currentNode.left == null && currentNode.right == null) {
                results.add(currentPath);
            }
            if (currentNode.right != null) {
                stack.push(currentNode.right);
                stack.push(currentPath + "->" + currentNode.right.val);
            }
            if (currentNode.left != null) {
                stack.push(currentNode.left);
                stack.push(currentPath + "->" + currentNode.left.val);
            }
        }
        return results;
    }
}

路径总和问题

判断是否存在路径总和等于目标值

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) return false;
        if (root.left == null && root.right == null) {
            return targetSum == root.val;
        }
        return hasPathSum(root.left, targetSum - root.val) || 
               hasPathSum(root.right, targetSum - root.val);
    }
}

找出所有路径总和等于目标值的路径

class Solution {
    public List<List<Integer>> findAllPaths(TreeNode root, int targetSum) {
        List<List<Integer>> allPaths = new ArrayList<>();
        if (root == null) return allPaths;
        List<Integer> currentPath = new LinkedList<>();
        currentPath.add(root.val);
        searchPaths(root, targetSum - root.val, allPaths, currentPath);
        return allPaths;
    }
    
    public void searchPaths(TreeNode node, int remainingSum, 
                          List<List<Integer>> allPaths, List<Integer> currentPath) {
        if (node.left == null && node.right == null && remainingSum == 0) {
            allPaths.add(new ArrayList<>(currentPath));
        }
        if (node.left != null) {
            currentPath.add(node.left.val);
            searchPaths(node.left, remainingSum - node.left.val, allPaths, currentPath);
            currentPath.remove(currentPath.size() - 1);
        }
        if (node.right != null) {
            currentPath.add(node.right.val);
            searchPaths(node.right, remainingSum - node.right.val, allPaths, currentPath);
            currentPath.remove(currentPath.size() - 1);
        }
    }
}

左叶子节点求和

class Solution {
    public int sumLeftLeaves(TreeNode root) {
        if (root == null) return 0;
        int leftValue = sumLeftLeaves(root.left);
        if (root.left != null && root.left.left == null && root.left.right == null) {
            leftValue = root.left.val;
        }
        int rightValue = sumLeftLeaves(root.right);
        return leftValue + rightValue;
    }
}

找树左下角的值

class Solution {
    int maxDepth = -1;
    int leftmostValue;
    
    public int findBottomLeftValue(TreeNode root) {
        findValue(root, 1);
        return leftmostValue;
    }

    void findValue(TreeNode node, int depth) {
        if (node.left == null && node.right == null) {
            if (depth > maxDepth) {
                maxDepth = depth;
                leftmostValue = node.val;
            }
            return;
        }
        if (node.left != null) {
            findValue(node.left, depth + 1);
        }
        if (node.right != null) {
            findValue(node.right, depth + 1);
        }
    }
}

二叉树构造问题

从中序和后序遍历构造二叉树

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if (postorder.length == 0) return null;
        
        int rootValue = postorder[postorder.length - 1];
        TreeNode root = new TreeNode(rootValue);
        
        if (postorder.length == 1) return root;
        
        int rootIndex = 0;
        for (; rootIndex < inorder.length; rootIndex++) {
            if (inorder[rootIndex] == rootValue) break;
        }
        
        int[] leftInorder = Arrays.copyOfRange(inorder, 0, rootIndex);
        int[] rightInorder = Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length);
        int[] leftPostorder = Arrays.copyOfRange(postorder, 0, leftInorder.length);
        int[] rightPostorder = Arrays.copyOfRange(postorder, leftInorder.length, postorder.length - 1);
        
        root.left = buildTree(leftInorder, leftPostorder);
        root.right = buildTree(rightInorder, rightPostorder);
        
        return root;
    }
}

从前序和中序遍历构造二叉树

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0) return null;
        
        int rootValue = preorder[0];
        TreeNode root = new TreeNode(rootValue);
        
        if (preorder.length == 1) return root;
        
        int rootIndex = 0;
        for (; rootIndex < inorder.length; rootIndex++) {
            if (inorder[rootIndex] == rootValue) break;
        }
        
        int[] leftInorder = Arrays.copyOfRange(inorder, 0, rootIndex);
        int[] rightInorder = Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length);
        int[] leftPreorder = Arrays.copyOfRange(preorder, 1, leftInorder.length + 1);
        int[] rightPreorder = Arrays.copyOfRange(preorder, leftInorder.length + 1, preorder.length);
        
        root.left = buildTree(leftPreorder, leftInorder);
        root.right = buildTree(rightPreorder, rightInorder);
        
        return root;
    }
}

构建最大二叉树

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return buildMaxTree(nums, 0, nums.length - 1);
    }
    
    public TreeNode buildMaxTree(int[] nums, int left, int right) {
        if (left > right) return null;
        if (left == right) return new TreeNode(nums[left]);
        
        int maxIndex = left;
        for (int i = left + 1; i <= right; i++) {
            if (nums[i] > nums[maxIndex]) {
                maxIndex = i;
            }
        }
        
        TreeNode root = new TreeNode(nums[maxIndex]);
        root.left = buildMaxTree(nums, left, maxIndex - 1);
        root.right = buildMaxTree(nums, maxIndex + 1, right);
        
        return root;
    }
}

合并二叉树

class Solution {
    public TreeNode mergeTrees(TreeNode tree1, TreeNode tree2) {
        if (tree1 == null) return tree2;
        if (tree2 == null) return tree1;
        
        tree1.val += tree2.val;
        tree1.left = mergeTrees(tree1.left, tree2.left);
        tree1.right = mergeTrees(tree1.right, tree2.right);
        
        return tree1;
    }
}

二叉搜索树问题

搜索二叉搜索树

class Solution {
    public TreeNode search(TreeNode root, int target) {
        if (root == null) return null;
        
        while (root != null) {
            if (root.val == target) return root;
            else if (root.val > target) root = root.left;
            else root = root.right;
        }
        
        return null;
    }
}

验证二叉搜索树

class Solution {
    public boolean isValid(TreeNode root) {
        return validate(root, null, null);
    }
    
    public boolean validate(TreeNode node, Integer min, Integer max) {
        if (node == null) return true;
        
        if ((min != null && node.val <= min) || (max != null && node.val >= max)) {
            return false;
        }
        
        return validate(node.left, min, node.val) && validate(node.right, node.val, max);
    }
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。