LeetCode做题思路记录(二叉树、哈希表、堆、图和动态规划)

二叉树

求和路径

题目:给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。

思路:用dfs + 回溯法

递归基:根节点为空

从当前根节点进行搜索,找到符合条件的路径则路径数量加一。递归左孩子、右孩子进行搜索。

求根到叶子节点数字之和

题目:给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。

例如,从根到叶子节点路径 1->2->3 代表数字 123。

计算从根到叶子节点生成的所有数字之和。

思路:使用DFS(或者说先序遍历),用 p r e v S u m prevSum prevSum 记录上一节点的“路径和”,当前节点和 s u m = p r e v S u m ∗ 10 + r o o t − > v a l sum = prevSum * 10 + root->val sum=prevSum10+root>val

若该节点为叶子节点,结束递归返回和。否则检索分叉路径,返回的和应该为 左子树 + 右子树

二叉树的深度

题目:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

思路:使用后序遍历,左右节点遍历完后再确定该节点的深度。关键是将空节点深度视为0。
也可使用层序遍历(广度优先搜索)(BFS),当遍历至最底层时即为深度。

最小高度树

题目:给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。

思路:递归

因为题目给了有序数组,因此直接从中间节点开始作为当前的根。然后递归构造左子树、右子树。

递归构造的时候注意传入数组的范围。

合并二叉树

题目:合并两棵二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

思路:递归

递归基:若树1节点 t 1 t1 t1 为空,则返回树2节点 t 2 t2 t2 t 2 t2 t2 为空则相反。

建立新节点,当前节点值为 t 1 − > v a l + t 2 − > v a l t1->val + t2->val t1>val+t2>val

左孩子指向递归合并 t 1 t1 t1 t 2 t2 t2 的左节点
后孩子则同时处理右节点。

平衡二叉树

题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

思路:求平衡二叉树的深度。

和求二叉树的深度类似。只不过要额外设置递归出口:向上回归的过程中:

  • 如果根节点的左右孩子深度差超过1,则立即返回-1(失败标志)
  • 如果根节点左右孩子有深度为-1,立即返回-1。

若最后返回的结果为-1,则该树为非平衡二叉树。

对称二叉树

题目:判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

思路
根据对称二叉树定义:

  • L . v a l = = R . v a l L.val == R.val L.val==R.val 左子树根节点等于右子树根节点
  • L . l e f t . v a l = = R . r i g h t . v a l L.left.val == R.right.val L.left.val==R.right.val L的左孩子等于R的右孩子
  • L . r i g h t . v a l = = R . l e f t . v a l L.right.val == R.left.val L.right.val==R.left.val L的右孩子等于R的左孩子

特例处理:root为空,返回true

递归判断树的每一对节点 (L,R):

  • 这一对节点都为空,即对称
  • 有一节点不为空,非对称
  • 都不为空,且值不等,非对称
  • 递推判断: ( L . l e f t , R . r i g h t ) (L.left, R.right) (L.left,R.right) && ( L . r i g h t , R . l e f t ) (L.right, R.left) (L.right,R.left)

重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

思路:递归

递归基:当传入的数组为空,则返回。

根据前序遍历序列确定根节点,在中序遍历序列中确定左右子树的数组范围。因此递归即可。

二叉树的镜像

题目:输入一个二叉树,该函数输出它的镜像。

思路:类似前序遍历

交换 根节点的左右孩子指针。

递归 获得左子树镜像,再 递归 获得右子树镜像(左右子树的递归顺序不影响)。

修剪二叉搜索树

题目:给定一个二叉搜索树,同时给定最小边界 L L L 和最大边界 R R R。通过修剪二叉搜索树,使得所有节点的值在 [ L , R ] [L, R] [L,R] 中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

思路:递归,类似先序遍历。
先处理根节点,再递归处理左子树和右子树。

递归:

  • 如果根节点为空,则返回空
  • 若根节点小于 L L L,则返回:递归处理右子树的根节点
  • 若根节点大于 R R R,则返回:递归处理左子树的根节点

根节点符合要求后,根节点的左孩子指向递归处理后的左子树,右孩子指向递归处理后的右子树。返回当前的根节点。

二叉树剪枝

题目:给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1。

返回移除了所有不包含 1 的子树的原二叉树。

题目理解:重复删除为0的叶节点

思路:由理解得,如果目前的根节点值为0,左子树,右子树为空,那么删除当前的根节点。

因此使用后序遍历,从下往上删除,使用 l e f t , r i g h t left, right left,right 接收递归的左右子树返回值。

递归基:根节点为空

返回值:当前的根节点

从上到下打印二叉树

题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

思路:广度优先搜索(BFS)

  • 先判断根节点是否为空
  • 将根节点入队 q q q
  • 队列非空则循环:
    • 访问首元素,若孩子非空,先左孩子入队,再右孩子入队
    • 首元素出队

从上到下打印二叉树 II

题目:从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

思路:广度优先搜索(BFS)

  • 先判断根节点是否为空
  • 根节点入队 q q q
  • 队列非空则循环
    • 先获取初始队列大小(此层的节点个数)
    • 循环访问此层节点,并记录到临时数组 t m p tmp tmp 中:若孩子非空则入队。
    • t m p tmp tmp 加入到结果数组 r e s res res
  • 返回 r e s res res

从上到下打印二叉树 III

题目
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

思路一:双端队列

设置变量 l e v e l level level 控制层数(根节点为第一层):

  • 奇数层:队首元素出队,元素的非空根节点按照从左到右入队尾
  • 偶数层:队尾元素出队,元素的非空根节点按照从右到左入队头

实质上类似使用队列和栈的结合体控制元素的输入输出,只不过想法并无那么直接。

思路二:通过控制奇数层顺序,偶数层逆序输出即可。

特定深度节点链表

题目:给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

思路:使用层序遍历,建立一个层次链表和尾插法解决遍历顺序的问题。

二叉树的最近公共祖先

题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:先分析公共祖先的性质:节点 p p p, q q q 应在其左子树或右子树,即先要确定左、右子树是否分别存在节点 p p p q q q ,然后才能确定该祖先是否是公共祖先。
最近公共祖先则可以使用递归,从底向上回溯时第一个找到的公共祖先则是最近的。

算法流程如下:

  • 递归基:根节点 r o o t root root,或节点 p p p q q q 为空
  • 设置返回值 l e f t left left 记录递归子树是否查找 p p p, q q q
  • 设置返回值 r i g h t right right 记录递归子树是否查找 p p p, q q q
  • 分类讨论:
    • 如果左右子树都为空,即该子树不存在 p p p, q q q,返回 n u l l p t r nullptr nullptr
    • 如果左子树为空,返回 r i g h t right right(说明该右子树有找到节点,但是目前还未找到公共祖先)
    • 如果右子树为空,返回 l e f t left left
    • 如果左右子树都非空,返回值根节点 r o o t root root 即为最近公共祖先

树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

思路:递归。类似后序遍历

递归基: A = = n u l l p t r ∣ ∣ B = = n u l l p t r A == nullptr || B == nullptr A==nullptrB==nullptr 返回 f a l s e false false

使用检查函数从A节点开始和B匹配:

  • 成功则返回 t r u e true true
  • 否则递归检查A的左孩子、右孩子,只要有一个节点匹配成功则返回 t r u e true true

检查函数:递归

  • B = = n u l l p t r B == nullptr B==nullptr 说明检查完毕,返回 t r u e true true。注意这里是检查函数,和上面 B = = n u l l p t r B == nullptr B==nullptr 的情况不一样。
  • 如果 A = = n u l l p t r A == nullptr A==nullptr,说明A已经越过叶节点,匹配失败。
  • 若两个节点值不同,匹配失败
  • 最后一个情况即当前节点匹配成功,递归匹配左孩子和右孩子。注意左右孩子都要匹配成功才可返回 t r u e true true

二叉搜索树的第k大节点

题目:给定一棵二叉搜索树,请找出其中第k大的节点。

思路:利用二叉搜索树的性质

中序遍历得到升序序列,返回倒数 k 个数即可。

二叉搜索树的范围和

题目:给定二叉搜索树的根结点 root,返回 L 和 R(含)之间的所有结点的值的和。

二叉搜索树保证具有唯一的值。

思路一:中序遍历序列

思路二:二叉树的搜索

二叉搜索树的最近公共祖先

题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

思路:(对二叉搜索树要有一定理解)

从根节点开始遍历

  • 若p,q的值都小于根节点,即在根节点左侧,递归搜索根的左孩子
  • 若都大于根节点,递归搜索根的右孩子
  • 其余情况:该根节点为最近公共祖先

二叉搜索树与双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

思路一:中序遍历序列

中序遍历用 v e c t o r vector vector 获取升序序列,然后在序列中调整左指针指向前一个元素,调整右指针指向后一个元素。(注意取模,考虑首尾元素的链接)

思路二:中序遍历递归(官方解法,略慢于思路一)

使用 p r e , h e a d pre, head pre,head 指针记录上一个节点和头节点。

当递归到最左边的元素时 p r e = c u r pre = cur pre=cur,该指针指向当前节点,并且用头节点记录。其余条件则按照要求链接节点即可。

二叉搜索树的后序遍历序列

题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同

思路:递归

递归基: p o s t o r d e r . s i z e ( ) < = 1 postorder.size() <= 1 postorder.size()<=1

确定左右子树:

  • 根据后序序列特征, r o o t = s i z e ( ) − 1 root = size()-1 root=size()1
  • 从后向左遍历数组,找到第一个小于根节点的元素 l e f t left left,即左子树的根节点
    因此右子树区间: [ l e f t + 1 , r o o t ) [left+1,root) [left+1,root)

遍历过程中,右子树确定所有元素大于根节点,因此只需要在遍历过程中确认左子树元素小于根节点。若大于,则返回 f a l s e false false

新建数组 l,r。将左右子树的元素分别复制进数组,然后递归判断左子树和右子树。

填充每个节点的下一个右侧节点

题目:给定一个完美二叉树。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

思路一:层序遍历(题目提示要常数空间,因此不是最优思路)

思路二:使用已建立的 n e x t next next 指针


哈希表

O(1) 时间插入、删除和获取随机元素 - 允许重复

题目:设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构。

注意: 允许出现重复元素。

insert(val):向集合中插入元素 val。
remove(val):当 val 存在时,从集合中移除一个 val。
getRandom:从现有集合中随机获取一个元素。每个元素被返回的概率应该与其在集合中的数量呈线性相关。

思路:使用哈希表 i d x idx idx 和顺序表 n u m num num,对每一个元素值 h a s h hash hash 到索引集合(在顺序表中的位置)上。顺序表用于记录元素值。

插入操作:顺序表尾插入,同时更新哈希表。

删除操作:

  • 判断哈希表中是否有 v a l val val
  • 获取哈希表中该值的索引 i n d e x index index
  • 用顺序表最后一个元素覆盖该元素
  • 更新哈希表:
    • 删除哈希表中 v a l val val 的索引 i n d e x index index
    • 删除哈希表中原本最后一个元素的索引 n u m [ i n d e x ] . s i z e ( ) − 1 num[index].size() - 1 num[index].size()1
  • 如果删除的元素不是最后一个元素,则对哈希表中原本最后一个元素的索引值进行更新,即插入 i n d e x index index
  • 如果删除元素后索引集合为空,则在删除哈希表中的键值 v a l val val

随机获取元素操作:随机获得一个索引,通过索引返回顺序表中的元素。

变位词组

题目:编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。

思路:遍历一次数组,将每个元素排序后映射到哈希表中,记录该元素对应的数组索引。

然后遍历哈希表,将相同映射结果的索引取出,添加到结果数组即可。


堆(Heap)

最小的k个数

题目:输入整数数组 arr ,找出其中最小的 k k k 个数。例如,输入

4、5、1、6、2、7、3、8

这8个数字,则最小的4个数字是

1、2、3、4

思路一:用STL建堆或者用优先队列模拟堆

时间复杂度: O ( n l o g k ) O(nlogk) O(nlogk)

用STL建堆注意语法即可,建完堆后依次将前k个元素弹出并每次维护堆结构。

优先队列模拟:

  • 初始化一个优先队列,含有k个元素(队首为最大值)
  • 对数组 [ k , a r r . s i z e ( ) ) [ k, arr.size() ) [k,arr.size()) 的部分进行判断,如果小于则队首元素出队,该元素进队。

思路二:快排分区思想

使用快排划分函数将数组以分界值 p i v o t pivot pivot 分成两部分,左边比 p i v o t pivot pivot 小,右边比 p i v o t pivot pivot 大。然后递归处理其中的一个分区:令 p o s pos pos 表示 p i v o t pivot pivot 的位置,令 l , r l, r l,r 表示正在处理的区间 $[l,r]。

  • 如果分区后 k = p i v i t k = pivit k=pivit,则该分界值为第k小元素
  • 假设 p o s > k pos > k pos>k 则表示第k小的元素在左区间, 否则在右区间
  • 特别的:若在右区间, p i v i t pivit pivit 是第 p o s − k + 1 pos - k + 1 posk+1 小的元素,则对右区间进行分区时,需要寻找的第k小元素是 k − ( p o s − k + 1 ) k - (pos - k + 1) k(posk+1) 的元素。

题目:在一个小镇里,按从 1 到 N 标记了 N 个人。传言称,这些人中有一个是小镇上的秘密法官。

如果小镇的法官真的存在,那么:

小镇的法官不相信任何人。
每个人(除了小镇法官外)都信任小镇的法官。
只有一个人同时满足属性 1 和属性 2 。
给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示标记为 a 的人信任标记为 b 的人。

如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1。

思路:用出度入度思考。
出度数组 o u t D e g r e e outDegree outDegree,入度数组 i n D e g r e e inDegree inDegree
遍历所给的 t r u s t trust trust 数组, t r u s t [ i [ [ 0 ] trust[i[[0] trust[i[[0] 即为第 i 个人的“出度”, t r u s t [ i ] [ 1 ] trust[i][1] trust[i][1] 则为入度。

遍历结束后根据遍历入度数组,寻找度数为 N - 1的人,若有则判断该人的出度是否为0。

节点间通路

题目:节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。注意:图中可能存在自环和平行边。

思路:使用 u n o r d e r e d _ s e t unordered\_set unordered_set 去重,使用 u n o r d e r e d _ m a p < i n t , u n o r d e r e d _ s e t < i n t > > unordered\_map<int, unordered\_set<int>> unordered_map<int,unordered_set<int>> 实现图的邻接表结构。然后使用 DFS 或者 BFS,用 v i s i t visit visit 数组判断该点是否已经遍历,解决图中有环的问题。

钥匙和房间

题目:有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。

在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。

最初,除 0 号房间外的其余所有房间都被锁住。

你可以自由地在房间之间来回走动。

如果能进入每个房间返回 true,否则返回 false

思路:简单DFS,设置观察数组 v i s i t visit visit 该房间有无被访问过,防止死循环。

从0开始,遍历整个房间获得钥匙,然后根据钥匙递归访问房间。


动态规划

单词拆分

题目:给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

思路:若 [ 0 , j ) [0, j) [0,j) 能被拆分,那么只需观察 [ j , s i z e ) [j, size) [j,size) 能否被拆分即可。用数组 d p [ i ] dp[i] dp[i] 表示长度为 i 的字串能否被成功切割。对于一个字串需要枚举所有可能的分割点 j j j,并且记录。

状态转移方程: d p [ i ] = d p [ j ] ∧ c h e c k [ j , i ) dp[i] = dp[j] \land check[j, i) dp[i]=dp[j]check[j,i) 其中:  0 < j < i \text{ 0 < j < i}  0 < j < i

对于判断一个字串是否在单词列表中可以用哈希表预处理。

视频拼接

题目:你将会获得一系列视频片段,这些片段来自于一项持续时长为 T 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。

视频片段 clips[i] 都用区间进行表示:开始于 clips[i][0] 并于 clips[i][1] 结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7] 可以剪切成 [0, 1] + [1, 3] + [3, 7] 三部分。

我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段 [ 0 , T ] [0, T] [0,T]。返回所需片段的最小数目,如果无法完成该任务,则返回 -1 。

题目理解:即在所给子区间内寻找能够覆盖 [ 0 , T ] [0,T] [0,T] 的区间

思路:动态规划

对时常为 T T T 秒的视频来说,若有子区间 [ a i , a j ] [a_i, a_j] [ai,aj] 满足 a i ≤ T ≤ a j a_i \leq T \leq a_j aiTaj,那么其能覆盖该视频的后半部分,那么只需考虑前半部分 [ 0 , a j ] [0, a_j] [0,aj] 即可。

因为要找最优解,所以长 T T T 的视频为 前半段的最小片段数量加一。

状态转移方程: d p [ T ] = m i n { d p [ a i ] } + 1 dp[T] = min\{dp[a_i]\} + 1 dp[T]=min{dp[ai]}+1 a i ≤ T ≤ a j a_i \leq T \leq a_j aiTaj

礼物的最大价值

题目:在一个 m*n 的棋盘(vector<vector>& grid)的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

边界情况

  • 第一行从第二个元素开始,每个元素加等于左一列元素的值
  • 第一列从第二各元素开始,每个元素加等于上一行元素的值

状态转移方程:

g r i d [ i ] [ j ] = { d p [ i ] [ j − 1 ] i = 0, j > 0 d p [ i − 1 ] [ j ] i > 0, j = 0 m a x ( g r i d [ i − 1 ] [ j ] , g r i d [ i ] [ j − 1 ] ) i > 0, j > 0 grid[i][j]= \begin{cases} dp[i][j-1]& \text{i = 0, j > 0}\\ dp[i-1][j] & \text{i > 0, j = 0}\\ max(grid[i-1][j],grid[i][j-1]) & \text{i > 0, j > 0} \end{cases} grid[i][j]=dp[i][j1]dp[i1][j]max(grid[i1][j],grid[i][j1])i = 0, j > 0i > 0, j = 0i > 0, j > 0

分割等和子集

题目:给定一个只包含正整数非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。(NP完全问题

类似 0-1 背包问题,可以理解为选取的数字(重量)恰好等于元素和(容量)的一半。

寻找目标: t a r g e t = s u m / 2 target = sum / 2 target=sum/2

基础条件的判断:

  • 若数组 n u m s nums nums 元素和 s u m sum sum 为奇数,则不可分割
  • 若最大元素 m a x E l e > t a r g e t maxEle > target maxEle>target ,不可分割

设二维数组 n n n t a r g e t + 1 target + 1 target+1 列, d p [ i ] [ j ] dp[i][j] dp[i][j] 表示是否存在从区间 [ 0 , i ] [0,i] [0,i] 内选取数字和为 j j j

边界情况:

  • j = 0 j = 0 j=0 时,不选取任何数即可, d p [ i ] [ 0 ] dp[i][0] dp[i][0] 都为真。
  • i = 0 i = 0 i=0 时,只能选取第一个整数, d p [ 0 ] [ n u m s [ i ] ] dp[0][nums[i]] dp[0][nums[i]] 为真。

其余情况:

  • j j j ≥ \geq n u m s [ i ] nums[i] nums[i]:则对 n u m s [ i ] nums[i] nums[i] 可以选择是否选取。任何一种情况成立则 d p [ i ] [ j ] dp[i][j] dp[i][j] 为真
    • 选取: d p [ i ] [ j ] = d p [ i − 1 ] [ j − n u m s [ i ] ] dp[i][j]=dp[i-1][j-nums[i]] dp[i][j]=dp[i1][jnums[i]]
    • 不选取: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j]
  • j < n u m s [ i ] j<nums[i] j<nums[i]:则不可选取当前值。 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]

状态转移方程:
d p [ i ] [ j ] = { d p [ i − 1 ] [ j ] j < nums[i] d p [ i − 1 ] [ j ] ∣ d p [ i − 1 ] [ j − n u m s [ i ] ] j  ≥  nums[i] dp[i][j]= \begin{cases} dp[i-1][j]& \text{j < nums[i]}\\ dp[i-1][j] | dp[i-1][j-nums[i]]& \text{j $\geq$ nums[i]} \end{cases} dp[i][j]={dp[i1][j]dp[i1][j]dp[i1][jnums[i]]j < nums[i] nums[i]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值