【算法自由之路】二叉树的递归套路

news2024/12/23 2:57:18

【算法自由之路】二叉树的递归套路

预热,二叉树的后继节点

话不多说,首先是一道练手题,寻找二叉树任意给定节点的后继节点,此二叉树具备一个指向父节点的指针。

后继节点:在中序遍历中于给定节点后一个打印的节点

    public static class TreeNode {

        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;

        public TreeNode(int val) {
            this.val = val;
        }
    }

这道题其实主要考验的是分类讨论和找规律的能力。首先理解中序遍历对每个子树打印顺序都是 左 中 右。比如下图这个树

做分类讨论其实就两个情况:

  1. 该节点有右节点:后继为右节点的最左节点
  2. 该节点没有右节点:后继节点为向上找,第一次子节点是父节点的左孩子的父节点,比如 8 的后继就是 4

对于情况 2 我们其实可以反推来理解, 4 节点的前驱节点应该是 左子树的最右的一个节点。

在这里插入图片描述

package algorithmic.base.tree;

/**
 * @program: algorithmic-total-solution
 * @description: 查找二叉树的后继节点,
 * @author: wangzibin
 * @create: 2023-01-10
 **/
public class FindNextNode {

    public static class TreeNode {

        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;

        public TreeNode(int val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "val=" + val +
                    '}';
        }
    }

    public static void print(TreeNode node) {
        if (node == null) {
            return;
        }
        print(node.left);
        System.out.print(node.val);
        print(node.right);
    }

    public static TreeNode findNextNode(TreeNode node) {
        if (node == null) {
            return null;
        }
        TreeNode result = null;
        // 如果有右孩子,后继节点是右孩子的最左节点
        if (node.right != null) {
            result = node.right;
            while (result.left != null) {
                result = result.left;
            }
            return result;
        }
        // 如果没有右孩子,向上找第一个子孩子是左节点的父节点 (有点绕,看代码)
        result = node.parent;
        // 注意最右节点没有后继节点
        while (node.parent != null && node != result.left) {
            node = node.parent;
            result = node.parent;
        }

        return result;
    }

    public static void main(String[] args) {
        TreeNode head = new TreeNode(2);
        TreeNode node1 = new TreeNode(1);
        TreeNode node3 = new TreeNode(3);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(5);
        TreeNode node6 = new TreeNode(6);
        TreeNode node7 = new TreeNode(7);
        TreeNode node8 = new TreeNode(8);
        head.left = node1;
        node1.parent = head;

        head.right = node3;
        node3.parent = head;

        node3.right = node4;
        node4.parent = node3;

        node4.left = node5;
        node5.parent = node4;

        node4.right = node6;
        node6.parent = node4;

        node5.right = node7;
        node7.parent = node5;

        node7.right = node8;
        node8.parent = node7;


        print(head);

        System.out.println();
        System.out.println(findNextNode(node1));
        System.out.println(findNextNode(head));
        System.out.println(findNextNode(node3));
        System.out.println(findNextNode(node5));
        System.out.println(findNextNode(node7));
        System.out.println(findNextNode(node8));
        System.out.println(findNextNode(node4));
        System.out.println(findNextNode(node6));
    }

}

递归套路

二叉树的递归套路有点像拆分子任务,将要求的最终结果分给每个分支去做

在实际应用中,对于一个整个树的问题,假设可以向左右子树要任何信息,整合信息后得出最终结果

整个拆分任务的操作我们交给了堆栈去做

递归套路 1 判断一个二叉树是平衡二叉树

平衡二叉树定义:对于树中任意一个节点,其左子树和右子树的高度差不超过 1。

这里假设我可以向左右子树获取任何信息,那对于判断我是否是平衡二叉树的关系信息是:

  1. 子树高度
  2. 子树是否是平衡二叉树

于是我可以定义这样一个返回结构

    public static class NodeInfo {
        public boolean isBalanced;
        public int high;

        public NodeInfo(boolean isBalanced, int high) {
            this.isBalanced = isBalanced;
            this.high = high;
        }
    }

最终代码很简单

public class BalanceTree {

    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    public static class NodeInfo {
        public boolean isBalanced;
        public int high;

        public NodeInfo(boolean isBalanced, int high) {
            this.isBalanced = isBalanced;
            this.high = high;
        }
    }

    public boolean isBalanced(TreeNode root) {
        return getNodeInfo(root).isBalanced;
    }

    public NodeInfo getNodeInfo(TreeNode node) {
        if (node == null) {
            return new NodeInfo(true, 0);
        }
        // 获取左子树信息
        NodeInfo left = getNodeInfo(node.left);
        // 获取右子树信息
        NodeInfo right = getNodeInfo(node.right);
        // 如果左右子树都平衡且高度差为 1 则我也平衡
        boolean isBalanced = left.isBalanced && right.isBalanced && Math.abs(left.high - right.high) <= 1;
        // 我的高度为左右子树最大高度 +1
        int high = Math.max(left.high, right.high) + 1;
        return new NodeInfo(isBalanced, high);
    }


}

可以直接到 leetcode 验证 平衡二叉树

感受到二叉树的递归套路的魅力了吗?关键点在于:

  1. 思考 当前 节点 与 左右子节点的关系 (一般的我们可以以 以 与当前节点有关与当前节点无关 来进行分类讨论 )
  2. 向左右子节点获取什么信息能够计算出我的信息
  3. 整合信息,定义结构

递归套路 2 计算给定树结构中二叉搜索子树的最大键和值

首先定义二叉搜索数 : 对于树中任意一个节点,其左树都小于该节点,右树都大于该节点

需要向左右节点要的信息:

  1. 树的最大值
  2. 树的最小值
  3. 是否是二叉搜索树
  4. 当前树的键和值
  5. 二叉搜索子树的最大键和值

在递归中就需要讨论最终结果是否与当前节点有关了,直接看代码

package algorithmic.base.tree;

import javax.xml.soap.Node;

/**
 * @program: algorithmic-total-solution
 * @description: 二叉搜索子树最大键和值  https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/
 * @author: wangzibin
 * @create: 2023-01-11
 **/
public class MaxSumBST {

    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    public static class NodeInfo {
        // 当前节点子树最大值
        private int max;
        private int min;
        private boolean isBst;
        private int bstSum;
        private int bstMaxSum;

        @Override
        public String toString() {
            return "NodeInfo{" +
                    "max=" + max +
                    ", min=" + min +
                    ", isBst=" + isBst +
                    ", bstSum=" + bstSum +
                    ", bstMaxSum=" + bstMaxSum +
                    '}';
        }
    }

    public static NodeInfo getInfo(TreeNode node) {
        if (node == null) {
            NodeInfo empty = new NodeInfo();
            empty.isBst = true;
            empty.min = Integer.MAX_VALUE;
            empty.max = Integer.MIN_VALUE;
            return empty;
        }
        NodeInfo leftInfo = getInfo(node.left);
        NodeInfo rightInfo = getInfo(node.right);
        NodeInfo current = new NodeInfo();
        current.max = max(node.val, leftInfo.max, rightInfo.max);
        current.min = min(node.val, leftInfo.min, rightInfo.min);
        // 判断是否与我有关, 当且仅当我也是二叉搜索树时最大值才与我有关,否则只需要比较子树值即可
        if (leftInfo.isBst && rightInfo.isBst && leftInfo.max < node.val && rightInfo.min > node.val) {
            // 左右子树都是搜索树,且 左子树最大值 小于 当前节点值 ,右子树最小值 大于 当前节点值,则可讨论与当前节点有关的情况
            current.isBst = true;
            current.bstSum = node.val + leftInfo.bstSum + rightInfo.bstSum;
            current.bstMaxSum = max(current.bstSum, leftInfo.bstMaxSum, rightInfo.bstMaxSum);
        } else {
            current.isBst = false;
            current.bstMaxSum = Math.max(leftInfo.bstMaxSum, rightInfo.bstMaxSum);
        }

        return current;
    }

    public static int max(int num1, int num2, int num3) {
        return Math.max(Math.max(num1, num2), num3);
    }

    public static int min(int num1, int num2, int num3) {
        return Math.min(Math.min(num1, num2), num3);
    }


    public static int maxSumBST(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int bstMaxSum = getInfo(root).bstMaxSum;
        return Math.max(bstMaxSum, 0);
    }

    public static void main(String[] args) {
        TreeNode treeNode1 = new TreeNode(1);
        treeNode1.left = null;
        TreeNode treeNode10 = new TreeNode(10);
        TreeNode treeNode5 = new TreeNode(-5);
        TreeNode treeNode20 = new TreeNode(20);
        treeNode1.right = treeNode10;
        treeNode10.left = treeNode5;
        treeNode10.right = treeNode20;
        System.out.println(maxSumBST(treeNode1));

    }

}

验证正确性 1373. 二叉搜索子树的最大键值和

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot实现配置文件的加密和解密

一、项目搭建 1.新建一个springBoot项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocatio…

DAMO-YOLO : A Report on Real-Time Object Detection Design

DAMO-YOLO:实时目标检测设计报告在本报告中&#xff0c;我们提出了一种被称为DAMO-YOLO的快速准确的物体检测方法&#xff0c;该方法比最先进的YOLO系列具有更高的性能。DAMO-YOLO是由YOLO扩展而来的&#xff0c;它采用了一些新技术&#xff0c;包括神经结构搜索(NAS)、高效的重…

LeetCode——1669. 合并两个链表

一、题目 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。 请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。 下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 来…

【Qt】3.菜单栏、工具栏、状态栏、铆接部件、核心部件、资源文件

目录 菜单栏 工具栏 代码 mainwindow.cpp 结果 状态栏 铆接部件 核心部件 代码 mainwindow.cpp 结果 资源文件 代码 mainwindow.cpp 结果 菜单栏 只能有一个 menuBar()返回一个QMenuBar *bar 将bar放入到窗口中setMenuBar(bar) 添加菜单addMenu("文件&…

三年了,回村了

今年回老家了&#xff0c;因为工作和疫情等原因已经三年多没回了&#xff0c;思乡之情已经压不住了。 老家是一个五线小城市&#xff0c;属于南方典型的鱼米之乡&#xff1a;依山傍水、山清水秀。同时还有一个知名白酒厂&#xff1a;白云边&#xff0c;经济发展还不错。 老家…

从“语义网”到“去中心化”,Web3.0到底是个啥?

什么是Web3.0&#xff0c;为什么近两年这个概念又再一次火出了圈&#xff0c;但凡A股上市公司正在做或者准备做的业务与它沾上边&#xff0c;总会有那么几次异动。 这个概念到底是金融市场布下的骗局&#xff0c;还是未来互联网发展的趋势&#xff0c;在大家的眼里都是褒贬不一…

Redis 核心原理串讲(下),架构演进之高扩展

文章目录Redis 核心原理总览&#xff08;全局篇&#xff09;前言一、数据分片1、集群&#xff1f;2、分片&#xff1f;3、分片固定&#xff1f;4、元数据二、集群1、代理集群2、分片集群3、代理 分片集群三、生产实践总结Redis 核心原理总览&#xff08;全局篇&#xff09; 正…

新的一年,如何打开超级APP发展格局

本文开始我们先来明确一个概念&#xff1a;超级APP是什么&#xff1f;百度百科的定义是——那些拥有庞大的用户数&#xff0c;成为用户手机上的"装机必备”的基础应用。实际上各大互联网平台也给出了不同的解释&#xff0c;但相同点是他们都认为超级APP就应该超级个性化&a…

简单手写后门Safedog检测绕过

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是简单手写后门Safedog检测绕过。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁…

最长上升子序列、最长公共子序列、最长公共上升子序列(LIS、LCS、LCIS)

LIS、LCS、LCIS最长上升子序列LIS最长公共子序列LCS最长公共上升子序列LCIS最长上升子序列LIS 题目链接&#xff1a;AcWing895. 最长上升子序列 这里只说明O(n2)O(n^2)O(n2)的解法&#xff0c;O(nlogn)O(nlogn)O(nlogn)解法之前的博客有介绍 O(n2)O(n^2)O(n2)的解法较为容易理…

MMCV - browse_dataset.py 可视化config文件数据预处理部分

无论是mmdetection、mmtracking、mmdetection3D等框架&#xff0c;在\tools\analysis_tools中均有一个名为browse_dataset.py的文件。该文件能够帮助用户直接可视化 config 文件中的数据处理部分&#xff0c;查看标注文件是否正确&#xff0c;同时可以选择保存可视化图片到指定…

百度学习经验

如何应对干扰1.将吸引你注意力的东西放在一旁&#xff0c;离开你的视野范围2.告诉大家你有空的时间&#xff0c;和你要专注的时间范围3.跟别人咨询的时候也是一样&#xff0c;不要在他专注的时候大扰别人关于如何做事&#xff0c;控制精力就是按照重要程度排序&#xff0c;不重…

《最佳停止时间》:什么时候可以停止寻找?

分享一个有趣的数学编程知识 《最佳停止时间》&#xff1a;什么时候可以停止寻找&#xff1f; 日常生活有很多"寻找-决策过程"&#xff0c;如果考察所有选项&#xff0c;要花费很长时间&#xff0c;可能还会错失机会&#xff0c;后面遇到的未必有前面的好。能否确定一…

实力见证 | Authing 荣获 2022 中国数字化转型与创新评选之“年度安全创新产品”

近日&#xff0c;由数字产业创新研究中心、锦囊专家、首席数字官联合全国 20 多家 CIO 组织机构、行业协会共同发起的 《2022 中国数字化转型与创新评选》 获奖榜单新鲜出炉&#xff0c;Authing 成功入选该榜单&#xff0c;荣获“年度安全创新产品”。 该榜单自 2022 年 5 月启…

【设计模式】行为型模式·策略模式

学习汇总入口【23种设计模式】学习汇总(数万字讲解体系思维导图) 一.概述 该模式定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以相互替换&#xff0c;且算法的变化不会影响使用算法的客户。 策略模式属于对象行为模式&#xff0c;它通过对算法进行封…

英语学习 作文 1

1 议论文和应用文 1、议论文&#xff1a;essay 1 观点选择&#xff1a;option、choice 2 现象解释&#xff1a;why、what 3 问题解决&#xff1a;how to、solution、measure **4 图片图表&#xff1a;image、cartoon、diagram、chart**2、应用文&#xff1a;信件、通知、报道…

设计模式——创建型模式

目录 4.创建型模式 4.1 单例设计模式 4.1.1 单例模式的结构 4.1.2 单例模式的实现 4.1.3 存在的问题 4.1.4 JDK源码解析-Runtime类 4.2 工厂模式 4.2.1 概述 4.2.2 简单工厂模式 4.2.3 工厂方法模式 4.2.4 抽象工厂模式 4.2.5 模式扩展 4.2.6 JDK源码解析-Collecti…

2023年山东最新建筑施工信号工(建筑特种作业)考试真题题库及答案

百分百题库提供特种工&#xff08;信号工&#xff09;考试试题、特种工&#xff08;信号工&#xff09;考试预测题、特种工&#xff08;信号工&#xff09;考试真题、特种工&#xff08;信号工&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助…

C++98以及C++11产生随机数的方法

目录引言1、C98标准实现随机数2、C11新标准随机数引擎引言 在C11出现之前&#xff0c;C98使用随机数采用的是C标准库的写法。而在C11出现后&#xff0c;我们生成随机数可以采用更为现代化的方式。 1、C98标准实现随机数 以往生成随机数使用的是cstdlib.h库中的rand()函数&…

基于Node.js的3DTiles三维倾斜摄影模型爬虫

随着小型无人机的普及,乡村级的倾斜摄影模型构建已经越来越简单。一个无人机和一名飞手2个小时内就可以完成。在做WebGIS和Cesium开发时,3DTiles是一种常用的倾斜摄影三维模型的切片格式。3DTiles格式通常有散列和紧凑两种文件组织形式,其中不同工具生成的散列数据使用的索引…