【leetcode热题100】不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

输入:n = 3
输出:5

示例 2:

输入:n = 1
输出:1

解法一 递归

下边是 95 题的分析。

我们可以利用一下查找二叉树的性质。左子树的所有值小于根节点,右子树的所有值大于根节点。

所以如果求 1...n 的所有可能。

我们只需要把 1 作为根节点,[ ] 空作为左子树,[ 2 ... n ] 的所有可能作为右子树。

2 作为根节点,[ 1 ] 作为左子树,[ 3...n ] 的所有可能作为右子树。

3 作为根节点,[ 1 2 ] 的所有可能作为左子树,[ 4 ... n ] 的所有可能作为右子树,然后左子树和右子树两两组合。

4 作为根节点,[ 1 2 3 ] 的所有可能作为左子树,[ 5 ... n ] 的所有可能作为右子树,然后左子树和右子树两两组合。

...

n 作为根节点,[ 1... n ] 的所有可能作为左子树,[ ] 作为右子树。

至于,[ 2 ... n ] 的所有可能以及 [ 4 ... n ] 以及其他情况的所有可能,可以利用上边的方法,把每个数字作为根节点,然后把所有可能的左子树和右子树组合起来即可。

如果只有一个数字,那么所有可能就是一种情况,把该数字作为一棵树。而如果是 [ ],那就返回 null。

对于这道题,我们会更简单些,只需要返回树的数量即可。求当前根的数量,只需要左子树的数量乘上右子树。

public int numTrees(int n) { if (n == 0) {return 0;}return getAns(1, n);} 
private int getAns(int start, int end) { int ans = 0;//此时没有数字,只有一个数字,返回 1if (start >= end) { return 1;} //尝试每个数字作为根节点for (int i = start; i <= end; i++) {//得到所有可能的左子树int leftTreesNum = getAns(start, i - 1);//得到所有可能的右子树int rightTreesNum  = getAns(i + 1, end);//左子树右子树两两组合ans+=leftTreesNum * rightTreesNum;}return ans;
}

受到这里的启发,我们甚至可以改写的更简单些。因为 95 题要把每颗树返回,所有传的参数是 start 和 end。这里的话,我们只关心数量,所以不需要具体的范围,而是传树的节点的数量即可。

public int numTrees(int n) { if (n == 0) {return 0;}return getAns(n);}private int getAns(int n) { int ans = 0;//此时没有数字或者只有一个数字,返回 1if (n==0 ||n==1) { return 1;} //尝试每个数字作为根节点for (int i = 1; i <= n; i++) {//得到所有可能的左子树// i - 1 代表左子树节点的数量int leftTreesNum = getAns(i-1);//得到所有可能的右子树//n - i 代表左子树节点的数量int rightTreesNum  = getAns(n-i);//左子树右子树两两组合ans+=leftTreesNum * rightTreesNum;}return ans;
}

然后,由于递归的分叉,所以会导致很多重复解的计算,所以使用 memoization 技术,把递归过程中求出的解保存起来,第二次需要的时候直接拿即可。

public int numTrees(int n) { if (n == 0) {return 0;}HashMap<Integer,Integer> memoization = new HashMap<>();return getAns(n,memoization);}private int getAns(int n, HashMap<Integer,Integer> memoization) { if(memoization.containsKey(n)){return memoization.get(n);}int ans = 0;//此时没有数字,只有一个数字,返回 1if (n==0 ||n==1) { return 1;} //尝试每个数字作为根节点for (int i = 1; i <= n; i++) {//得到所有可能的左子树int leftTreesNum = getAns(i-1,memoization);//得到所有可能的右子树int rightTreesNum  = getAns(n-i,memoization);//左子树右子树两两组合ans+=leftTreesNum * rightTreesNum;}memoization.put(n, ans);return ans;
}

解法二 动态规划

直接利用95题解法三的思路,讲解比较长就不贴过来了,可以过去看一下。

或者直接从这里的解法一的思路考虑,因为递归是从顶层往下走,压栈压栈压栈,到了长度是 0 或者是 1 就出栈出栈出栈。我们可以利用动态规划的思想,直接从底部往上走。求出长度是 0,长度是 1,长度是 2....长度是 n 的解。用一个数组 dp 把这些结果全部保存起来。

public int numTrees(int n) {int[] dp = new int[n + 1];dp[0] = 1;if (n == 0) {return 0;}// 长度为 1 到 nfor (int len = 1; len <= n; len++) {// 将不同的数字作为根节点,只需要考虑到 lenfor (int root = 1; root <= len; root++) {int left = root - 1; // 左子树的长度int right = len - root; // 右子树的长度dp[len] += dp[left] * dp[right];}}return dp[n];
}

参考这里还有优化的空间。

利用对称性,可以使得循环减少一些。

  • n 是偶数的时候 1 2 | 3 4 ,for 循环中我们以每个数字为根求出每个的解。我们其实可以只求一半,根据对称性我们可以知道 1 和 4,2 和 3 求出的解分别是相等的。

  • n 是奇数的时候

    1 2 | 3 | 4 5,和偶数同理,只求一半,此外最中间的 3 的解也要加上。

public int numTrees6(int n) {if (n == 0) {return 0;}int[] dp = new int[n + 1];dp[0] = 1;dp[1] = 1;// 长度为 1 到 nfor (int len = 2; len <= n; len++) {// 将不同的数字作为根节点,只需要考虑到 lenfor (int root = 1; root <= len / 2; root++) {int left = root - 1; // 左子树的长度int right = len - root; // 右子树的长度dp[len] += dp[left] * dp[right];}dp[len] *= 2;// 利用对称性乘 2// 考虑奇数的情况if ((len & 1) == 1) {int root = (len >> 1) + 1;int left = root - 1; // 左子树的长度int right = len - root; // 右子树的长度dp[len] += dp[left] * dp[right];}}return dp[n];
}

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

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

相关文章

算法学习——LeetCode力扣回溯篇2

算法学习——LeetCode力扣回溯篇2 40. 组合总和 II 40. 组合总和 II - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字…

机器视觉与智能制造:开启工业自动化的新篇章

机器视觉与智能制造的结合&#xff0c;无疑为工业自动化开启了一个全新的篇章。这两者的结合&#xff0c;不仅提高了生产效率&#xff0c;降低了成本&#xff0c;还提升了产品质量&#xff0c;增强了企业的市场竞争力。 一、机器视觉的核心技术 机器视觉是一种模拟人类视觉的…

Qt 的准备知识

文章目录 1. Qt 背景介绍2. 搭建 Qt 开发环境3. 认识 Qt Creator3.1 main.cpp3.2 widget.h3.3 widget.cpp3.4 Forms3.5 .pro文件 1. Qt 背景介绍 Qt 是⼀个 跨平台的 C 图形用户界面应用程序框架 。它为应用程序开发者提供了建立艺术级图形界⾯所需的所有功能。它是完全⾯向对…

电脑设置静态地址有啥用

随着信息技术的迅猛发展&#xff0c;计算机网络已成为现代社会不可或缺的基础设施。在网络世界中&#xff0c;每一台计算机都需要一个独特的标识来与其他计算机进行通信&#xff0c;这个标识就是IP地址。其中&#xff0c;静态IP地址作为一种固定的网络配置方式&#xff0c;在特…

C语言-----习题

1.通过这个例题&#xff0c;我们可以知道*p.a是无法打印99的&#xff0c;因为.的优先级比解引用*高&#xff1b; ​ struct S {int a;int b; }; int main() {struct S a, * p &a;//可以分为两部分理解//struct S a;//struct S *p &a;a.a 99;printf("%d\n"…

Docker 在window 2024版笔记 下载 安装 操作 配置

---Docker 前言--- Docker windows版官方版是一款专业开源的应用容器引擎&#xff0c;可以加快用户构建、共享和运行现代应用程序的速度&#xff0c;支持运行Linux和Windows Docker容器。 Docker 在容器的基础上&#xff0c;进行了进一步的封装&#xff0c;从文件系统、网络互…

2024年华为OD机试真题-密码输入检测-Java-OD统一考试(C卷)

题目描述: 给定用户密码输入流input,输入流中字符<表示退格,可以清除前一个输入的字符,请你编写程序,输出最终得到的密码字符,并判断密码是否满足如下的密码安全要求。 密码安全要求如下: 1.密码长度>=8; 2.密码至少需要包含1个大写字母; 3.密码至少需要包含1个小…

关于Windows Media Player的一些知识,看这篇文章就差不多了

你知道如何在电脑上打开Windows Media Player吗?如果它不是你电脑上默认的媒体播放器,你知道如何将其设为默认吗?此外,如果你找不到它,你知道怎么把它找回来吗?这篇文章将向你展示你想要了解的所有信息。 在这篇文章中,我们将向你展示以下信息: 如何打开Windows Medi…

从傅立叶变换到奥运五环

傅立叶变换的本质 离散傅立叶变换的本质就是用一系列的正弦&#xff08;或者余弦&#xff09;函数的和来拟合一个周期函数f(t)。公式如下&#xff1a; (1) 其中T是f(t)的周期&#xff0c;即f(tT)f(t)对任意t成立。和的周期是。换句话说&#xff0c;后两者的频率是f(t)的频率的…

推荐10个最受欢迎的 Vue.js UI 库

在2024年,随着Vue.js的不断普及和发展,这个轻量级、易于学习的JavaScript框架在前端开发者中的受欢迎程度日益上升。Vue.js之所以受到青睐,很大一部分原因是其庞大的生态系统,特别是众多的UI库,这些库提供了预先构建的组件和工具,帮助开发者快速高效地构建出既美观又响应…

Python算法深度探索:从基础到进阶

引言 本文将引导您从Python的基础算法出发&#xff0c;逐步深入到更复杂的算法领域。我们将探讨数组操作、图算法以及机器学习中的常用算法&#xff0c;并通过实例和代码展示它们在实际应用中的价值。 1. 基础算法&#xff1a;数组操作 数组操作是算法实现中非常基础且重要的一…

Editing While Playing 使用 Easyx 开发的 RPG 地图编辑器 tilemap eaitor

AWSD移动画布 鼠标右键长按拖拽 鼠标左键长按绘制 可以边拖拽边移动画布边绘制。 F1 导出 DLC F2 导入DLC author: 民用级脑的研发记录 1309602336qq.com 开发环境&#xff1a; 内置 easyx 的 devc 5.11 或者 VS 2022 TDM GCC 4.9.2 64-bit c11及以上都可运行 windows 环境运行…

算法-16-并查集

并查集简介 并查集&#xff1a;一开始&#xff0c;把a&#xff0c;b&#xff0c;c放入并查集&#xff0c;a自己一个集合&#xff0c;b自己一个&#xff0c;c自己一个 提供的方法 1.boolean isSameSet(a,b)&#xff0c;判断ab是否在同一个集合 2.void union(a,b),把a所…

Hadoop-Yarn-ResourceManagerHA

在这里先给屏幕面前的你送上祝福&#xff0c;祝你在未来一年&#xff1a;技术步步高升、薪资节节攀升&#xff0c;身体健健康康&#xff0c;家庭和和美美。 一、介绍 在Hadoop2.4之前&#xff0c;ResourceManager是YARN集群中的单点故障 ResourceManager HA是通过 Active/St…

【深度学习】S2 数学基础 P3 微积分(上)导数与微分

目录 圆与微积分导数与微分导数的含义数学定义常用函数微分常用微分法则Python 实现 圆与微积分 公元前 2500 年&#xff0c;古希腊数学家阿基米德通过一种名为 “逼近法” 的技巧来估算圆的面积。他采用一个有奇数边的正多边形来外切圆&#xff0c;并用一个有偶数边的正多边形…

【经验】STM32的一些细节

这两天 碰到的奇葩问题是 STM32定时器同步的问题。 我的设计本意是&#xff1a;使用定时器T3以100us的周期来定时发送命令给 FPGA。由于编码器出结果的最长时间为51us。因此&#xff0c;希望PWM中断要滞后于T3 约60us 。 调试过程&#xff1a;分别在T3和PWM中断中置IO1&#…

163基于matlab的不同目标函数的盲源信号分离基于负熵的

基于matlab的不同目标函数的盲源信号分离基于负熵的&#xff1b;基于负熵的改进算法&#xff1b; 基于峭度的&#xff1b;基于互信息的&#xff1b;基于非线性PCA的。输出解混前后信号结果。程序已调通&#xff0c;可直接运行。 163 负熵、峭度、互信息、PCA 信号处理 (xiaohon…

牛客——递归实现组合型枚举(枚举,dfs)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 从 1~n 这 n 个整数中随机选出 m 个&#xff0c;输出所有可能的选择方案。n>0n \gt 0n>0, 0≤m≤n0 \leq m \leq n0≤m≤n, n(n−m)≤25n(n-m)\leq 25n(n−m)≤25。 输入描述…

多种C语言结构体赋值方法介绍

C语言中&#xff0c;结构体&#xff08;structure&#xff09;是一种用户自定义的复合数据类型&#xff0c;它允许以一种有组织的方式存储不同类型的数据。结构体的赋值是对结构体成员进行赋值的过程。在C语言中&#xff0c;有多种不同的方法可以对结构体进行赋值&#xff0c;下…

LeetCode Python - 17.电话号码的字母组合

目录 题目答案运行结果 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits…