【图解】从一道动态规划题带你领略『卡特兰数』是如何秒杀算法题的

2023-10-11 15:50

本文主要是介绍【图解】从一道动态规划题带你领略『卡特兰数』是如何秒杀算法题的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、 leetcode 96 号题

题目链接:https://leetcode-cn.com/problems/unique-binary-search-trees/

题意:给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

n = 3 时:

二、动态规划

思路:从 1 开始到 n ,每次以这个数为根,左子树存放比它小的数,右子树存放比它大的数。每个根不重复,因此每个树也必定不重复。

左子树和右子树,又可以按照这个规则去生成新的树。

例如:n = 3的时候

1为根: 比 1 小的数只有 0,不用管。比 1 大的数有 2 和 3。

拿 2 和 3 来生成一棵树和拿 1 和 2 来生成一棵树的种数是不是相同的?那么 1 和 2 能生成多少种树呢?

2为根: 比 2 小的是 1,比 2 大的是 3。这里只有 1 种。

3为根: 比 3 小的是 1 和 2,1 和 2 能生成多少种树呢?

我们先暂停思维,来到一个新的问题。 n = 2 的时候,结果应该是多少?

n = 2 的时候,按照我们之前的方法。

1 为根:比 1 大的数只有 2, 这里有 1 种。
2 为根:比 2 小的数只有 1, 这里有 1 种。

那答案就应该是 2 种。

解决了 n = 2 的问题,那 n = 3 的问题就也解决了。 ans = 2 + 1 + 2 = 5。

我们来看一般情况。输入一个 n 。

定义一个 F(i) 表示以 i 为根,生成的树的种数。

定义一个 G(n) 表示输入 n 的时候,输出的结果。此处一定要注意 F 与 G 的区别。

以 i 为根的时候,能生成多少种树?

比 i 小的有 i - 1 个,比 i 大的有 n - i 个。因此左子树有 i - 1 个, 右子树有 n - i 个数。那么,F(i) = G(i - 1) * G(n - i)。
3
而我们求的 G(n) = F(1) + F(2) + …… + F(n)。

把两个公式综合 :

G(n) = ∑ i = 1 n G ( i − 1 ) ∗ G ( n − i ) \displaystyle \sum^{n}_{i = 1}{G(i-1)}*G(n-i) i=1nG(i1)G(ni)

d[0] = 1; // 0 的时候特殊处理
for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++) d[i] += d[j-1] * d[i-j]; 

以上是利用动态规划求解的思路。

时间复杂度:O(n^2)

空间复杂度:O(n)

三、 Catalan公式

这个题目还有一种很强的解法,卡特兰公式。卡特兰公式和排列组合有很大关系,不属于偏难怪解法,有很多算法和数据结构的问题本质上就是卡特兰公式的应用。比如二叉树的形态数,出栈序列数,括号匹配问题等。公式不要紧,没必要去硬背。主要是理解卡特兰问题应用的特征,把问题抽象到已有模型中来。

Catalan 递推项满足:

C(n) = C(0) * C(n-1) + C(1) * C(n-2) + … + C(n-2) * C(1) + C(n-1) * C(0)

Catalan 通项公式: C n C_{n} Cn = 1 n + 1 {1}\over {n+1} n+11 C 2 n n C_{2n}^{n} C2nn

Catalan 递推公式1: C n + 1 C_{n+1} Cn+1 = 4 n + 2 n + 2 {4n + 2}\over {n + 2} n+24n+2 C n C_{n} Cn

Catalan 性质: C n C_{n} Cn = C 2 n n C_{2n}^{n} C2nn - C 2 n n − 1 C_{2n}^{n-1} C2nn1

这个题目里面,由我们上面的 G(n) 很容易可以看出是一个卡特兰的应用。

我们用它的递推公式来求解。

C n C_{n} Cn 的值, C n + 1 C_{n+1} Cn+1 = 4 n + 2 n + 2 {4n + 2}\over {n + 2} n+24n+2 C n C_{n} Cn。公式顺推,代码中 n 次循环结束后的 ans 值就是 C n C_{n} Cn 对应的值。类比斐波那契最简解法。

	long ans = 1;for(int i = 1; i <= n; i++)ans = ans * 2 * (2 * i + 1) / (i + 2);return (int) ans;

时间复杂度: O(n)

空间复杂度: O(1)

四、Catalan应用

例题1:括号序列

给 n 对括号,可以合成的合法序列有多少种?

  1. 首先计算一共的序列种数。n 对括号, 一共有 2n 个位置。我们选出其中 n 个位置放放置左括号,剩下的位置肯定就是右括号了。因此一共的种数为: C 2 n n C_{2n} ^ {n} C2nn
  2. 接下来找出非法的括号序列数。每个非法的序列,在它的奇数位置,一定存在右括号数量大于左括号的数量。

在上图中:第 2m + 1 的时候,右括号大于左括号,因此该序列非法。

在 2m + 1 前:

右括号数量为: m + 1

左括号数量为: m

在 2m + 1后:

总的数量: n - 2m - 1

左括号有: n - 2m - 1 - (m + 1) = n - m

右括号有: n - 2m - 1 - m = n - m - 1

这个时候我们将右边的左括号和右括号位置置换(总的组合数量不会受到影响)。那么在整个序列 2n 个位置中:

右括号的数量为: m + 1 + n - m = n + 1

左括号的数量为: m + n - m - 1 = n - 1

在长度为 2n 里面有 n + 1 个右括号,数量为: C 2 n n + 1 C_{2n} ^ { n+1} C2nn+1 。你也可以理解从左括号的角度去看: C 2 n n − 1 C_{2n} ^ {n-1} C2nn1

在上面两个步骤以后,我们得到的合法序列数: C 2 n n C_{2n} ^ {n} C2nn - C 2 n n − 1 C_{2n} ^ {n-1} C2nn1 。这就是 Catalan 公式的性质公式。知道是 Catalan,我们就可以用刚刚的方法求解问题的答案。

例题2 :一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?

例题3 :给出一个n,要求一个长度为2n的01序列,使得序列的任意前缀中1的个数不少于0的个数, 有多少个不同的01序列?

例题4 :2n个人要买票价为五元的电影票,每人只买一张,但是售票员没有钱找零。其中,n个人持有五元,另外n个人持有十元,问在不发生找零困难的情况下,有多少种排队方法?(阿里有个笔试题根据这个变化的)

兄dei,如果觉得我写的不错,不妨帮个忙

1、关注我的原创微信公众号「帅地玩编程」,每天准时推送干货技术文章,专注于写算法 + 计算机基础知识(计算机网络+ 操作系统+数据库+Linux),听说关注了的不优秀也会变得优秀哦。

2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我,嘻嘻。

作者简洁

作者:大家好,我是帅地,从大学、自学一路走来,深知算法计算机基础知识的重要性,所以申请了一个微星公众号『帅地玩编程』,专业于写这些底层知识,提升我们的内功,帅地期待你的关注,和我一起学习。 转载说明:未获得授权,禁止转载

这篇关于【图解】从一道动态规划题带你领略『卡特兰数』是如何秒杀算法题的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/189122

相关文章

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Python Selenium动态渲染页面和抓取的使用指南

《PythonSelenium动态渲染页面和抓取的使用指南》在Web数据采集领域,动态渲染页面已成为现代网站的主流形式,本文将从技术原理,环境配置,核心功能系统讲解Selenium在Python动态... 目录一、Selenium技术架构解析二、环境搭建与基础配置1. 组件安装2. 驱动配置3. 基础操作模

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删