21-100、相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:       1         1
          / \       / \
         2   3     2   3

[1,2,3],   [1,2,3]

输出: true

递归:

时间复杂度 : O(N)O(N),其中 N 是树的结点数,因为每个结点都访问一次。

空间复杂度 : 最优情况(完全平衡二叉树)时为 O(\log(N))O(log(N)),最坏情况下(完全不平衡二叉树)时为 {O}(N)O(N),用于维护递归栈。

class Solution:def isSameTree(self, p, q):""":type p: TreeNode:type q: TreeNode:rtype: bool"""    # p and q are both Noneif not p and not q:return True# one of p and q is Noneif not q or not p:return Falseif p.val != q.val:return Falsereturn self.isSameTree(p.right, q.right) and \self.isSameTree(p.left, q.left)

22-101、对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

1
   / \
  2   2
 / \ / \
3  4 4  3

递归实现
乍一看无从下手,但用递归其实很好解决。
根据题目的描述,镜像对称,就是左右两边相等,也就是左子树和右子树是相当的。
注意这句话,左子树和右子相等,也就是说要递归的比较左子树和右子树。
我们将根节点的左子树记做left,右子树记做right。比较left是否等于right,不等的话直接返回就可以了。
如果相当,比较left的左节点和right的右节点,再比较left的右节点和right的左节点
比如看下面这两个子树(他们分别是根节点的左子树和右子树),能观察到这么一个规律:
左子树2的左孩子 == 右子树2的右孩子
左子树2的右孩子 == 右子树2的左孩子

2         2
   / \       / \
  3   4     4   3
 / \ / \   / \ / \
8  7 6  5 5  6 7  8
根据上面信息可以总结出递归函数的两个条件:
终止条件:

left和right不等,或者left和right都为空
递归的比较left.left和right.right,递归比较left.right和right.left

class Solution(object):def isSymmetric(self, root):""":type root: TreeNode:rtype: bool"""if not root:return Truedef dfs(left,right):# 递归的终止条件是两个节点都为空# 或者两个节点中有一个为空# 或者两个节点的值不相等if not (left or right):return Trueif not (left and right):return Falseif left.val!=right.val:return Falsereturn dfs(left.left,right.right) and dfs(left.right,right.left)# 用递归函数,比较左节点,右节点return dfs(root.left,root.right)

23-104、二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

class Solution:def maxDepth(self, root: TreeNode) -> int:if root== None:return 0if root.left == None and root.right == None:return 1lh = self.maxDepth(root.left)rh = self.maxDepth(root.right)return max(rh,lh) + 1

24-107、二叉树的层次遍历

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7
返回其自底向上的层次遍历为:

[ [15,7], [9,20], [3] ]

队列:

class Solution:def levelOrderBottom(self, root):queue = []                                                  cur = [root]                                  # 接下来要循环的当前层节点,存的是节点while cur:                                    # 当前层存在结点时cur_layer_val = []                       # 初始化当前层结果列表为空,存的是valnext_layer_node = []                      # 初始化下一层结点列表为空for node in cur:                          # 遍历当前层的每一个结点if node:                              # 如果该结点不为空,则进行记录cur_layer_val.append(node.val)    # 将该结点的值加入当前层结果列表的末尾next_layer_node.extend([node.left, node.right]) # 将该结点的左右孩子结点加入到下一层结点列表if cur_layer_val:                         # 只要当前层结果列表不为空queue.insert(0, cur_layer_val)        # 则把当前层结果列表插入到队列首端cur = next_layer_node                     # 下一层的结点变成当前层,接着循环return queue                                  # 返回结果队列

25-108、将有序数组转化为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

0
     / \
   -3   9
   /   /
 -10  5

解析:

平衡二叉搜索树需要保证俩点:
           根节点大于左子树任意节点,小于右子树任意节点
          左右子数高度相差不超过 1
由以上性质,一个可行的递归条件可以得出:
           每次返回的根节点处于数组中间,以其左右半数组分别递归构造左右子树
            那么就意味着左子小于根,右子大于根,且所有节点左右子树节点数相差不超过 1 (由于递归的构树方式相同,所有节                 点都满足高度平衡)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = Noneclass Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:if nums:m = len(nums) // 2r = TreeNode(nums[m])r.left, r.right = map(self.sortedArrayToBST, [nums[:m], nums[m+1:]])return r

26-110、平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

3
   / \
  9  20
    /  \
   15   7
返回 true 。

class Solution:def isBalanced(self, root: TreeNode) -> bool:if not root:return Truereturn abs(self.height(root.right)-self.height(root.left))<2 and self.isBalanced(root.left) and self.isBalanced(root.right)# 求高度def height(self, node):if not node:return 0return 1+max(self.height(node.right),self.height(node.left))

27-111、平衡二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7
返回它的最小深度  2.

这道题和maximum depth题正好相反,是求根节点到叶子节点的最小深度,为确保统计的是根节点到叶子节点的深度,需要注意:
当前节点左右子树有一个为空时,返回的应是非空子树的最小深度,而不是空子树深度0;若返回0相当于把当前节点认为成叶子节点,与此节点有非空子树矛盾。
当左右子树都不为空时,和maximum depth题一样,返回左右子树深度的最小值。
当左右子树都为空时,只有1个根节点深度为1(根节点与叶子节点重合)。

class Solution:def minDepth(self, root: TreeNode) -> int:if not root: return 0if not root.left: return self.minDepth(root.right) + 1if not root.right: return self.minDepth(root.left) + 1return min(self.minDepth(root.left), self.minDepth(root.right)) + 1

28-112、路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

示例: 
给定如下二叉树,以及目标和 sum = 22,

5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

递归:

def hasPathSum(self, root: TreeNode, sum: int) -> bool:if not root: return Falseif not root.left and not root.right and sum - root.val == 0:return Truereturn self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)

29-118、杨辉三角

给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 5
输出:
[
     [1],
    [1,1],
   [1,2,1],
  [1,3,3,1],
 [1,4,6,4,1]
]

可以画一个表格数组

class Solution:def generate(self, numRows: int) -> List[List[int]]:if numRows <= 0: return []res = []for row in range(numRows):if row == 0:res.append([1])else:tmp = [1]for c in range(row):# 当c == row-1时, 表示当前列为倒数第一个元素, 该元素与哨兵位置的0元素相加. sum_ = (res[row-1][c] + 0) if (c == row-1) else (res[row-1][c]+res[row-1][c+1])tmp.append(sum_)res.append(tmp)return res

30-119、杨辉三角2

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 3
输出: [1,3,3,1]
进阶:你可以优化你的算法到 O(k) 空间复杂度吗?

def getRow(rowIndex):# j行的数据, 应该由j - 1行的数据计算出来.# 假设j - 1行为[1,3,3,1], 那么我们前面插入一个0(j行的数据会比j-1行多一个),# 然后执行相加[0+1,1+3,3+3,3+1,1] = [1,4,6,4,1], 最后一个1保留即可.r = [1]for i in range(1, rowIndex + 1):r.insert(0, 0)# 因为i行的数据长度为i+1, 所以j+1不会越界, 并且最后一个1不会被修改.for j in range(i):r[j] = r[j] + r[j + 1]return r

31-121、买卖股票的最佳时机①-买卖一次

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

动态规划:最大价格=max(前一天的最大利润,今天价格-之前的最小价格)

class Solution:def maxProfit(self, prices: List[int]) -> int:if len(prices) <= 1: return 0min_price = prices[0]max_p = 0for i in range(len(prices)):min_price = min(min_price,prices[i])max_p = max(max_p,prices[i]-min_price)return max_p

122、买卖股票的最佳时机②-买卖多次

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

等价于每天都买卖,即把正数差值相加即为最大利润:[-6,4,-2,3,-2],最大为4+3=7

  • 复杂度分析:

    • 时间复杂度 O(N)O(N) : 只需遍历一次price
    • 空间复杂度 O(1)O(1) : 变量使用常数额外空间。
class Solution:def maxProfit(self, prices: List[int]) -> int:max_p=0for i in range(1,len(prices)):tmp = prices[i]-prices[i-1]if tmp > 0:max_p += tmpreturn max_p

123、买卖股票的最佳时机③--最多两笔交易

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

四种状态:buy1[i]表示前i天第一次交易买入后剩下最多的钱;shell1[i]表示前i天第一次交易卖出后剩下最多的钱;

buy2[i]表示前i天第二次交易买入后剩下最多的钱; shell2[i]表示前i天第二次交易卖出后剩下最多的钱;

则:buy1[i] = max{buy1[i-1],,-prices[i]}

shell[i]=max{shell1[i-1],buy1[i-1]+prices[i]}

buy2[i] = max{buy2[i-1],shell2[i-1] - prices[i]}

shell2[i] = max{shell2[i-1],buy2[i-1]+prices[i]}

from sys import maxsize
class Solution:def maxProfit(self, prices: List[int]) -> int:buy1,sell1,buy2,sell2 = -maxsize,0,-maxsize,0for i in range(len(prices)):buy1 = max(buy1,-prices[i])sell1 = max(sell1,buy1+prices[i])buy2 = max(buy2,sell2-prices[i])sell2 = max(sell2,buy2+prices[i])return sell2

188、买卖股票的最佳时机④--最多K次

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

解:由于188题有限制条件,即最多只能交易k次,还需要记录交易次数,还需要一维来记录交易了多少次
此时状态方程变成了

mp[i][h][k]
i 表示天数
j 表示是否持有股票
k 表示之前交易了多少次
此时动态规划的转移方程变成了如下所示:

// 为了方便理解 把k放到了第二维

//前一天 要么不操作 要么卖掉了一股
mp[i][k][0] = max(mp[i-1][k][0], mp[i-1][k-1][1] + a[i])

//  前一天 要么不操作 要么买入了一股
mp[i][k][1] = max(mp[i-1][k][1], mp[i-1][k][0] - a[i)
想要求出最大的收益 只需要找到
mp[n-1, {0...k},0]的最大值即可

class Solution:def maxProfit(self, k: int, prices: List[int]) -> int:if not prices or not k:return 0n  = len(prices)# 如果k大于数组长度的一半,则可以用贪心解决if k > n//2:return self.greedy(prices)# 动态规划dp = [[[0] * 2 for _ in range(k+1)] for _ in range(n)]res = []# 设置初始状态for i in range(k+1):dp[0][i][0], dp[0][i][1] = 0, -prices[0]# 开始两层循环for i in range(1,n):for j in range(k+1):if not j:dp[i][j][0] = dp[i-1][j][0]else:dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j-1][1] + prices[i])dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j][0] - prices[i])# 找到最大值for m in range(k+1):print(dp[n-1][m][0])res.append(dp[n-1][m][0])return max(res)def greedy(self, prices):res = 0for i in range(1,len(prices)):if prices[i] > prices[i-1]:res += prices[i] - prices[i-1]return res

309、买卖股票最佳时期含冷冻期

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:

输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

class Solution:def maxProfit(self, prices: List[int]) -> int:if not prices:return 0# 每个元祖表示当前持有股票以及不持有股票时所能最大化的自己的余额temp = (-prices[0], 0)  # 对第一个股价,若持有时余额为负股票价,不持有时为0if len(prices) == 1:return max(temp)dp = [temp]# 持有时余额最大为前两天最低价买入的情况,不持有时为一直不买和一买二卖中的最大值temp = (-min(prices[0], prices[1]), max(0, prices[1] - prices[0]))dp.append(temp)# 持有时考虑保持前一天的持有和前两天不持有现在买入时的情况,不持有时考虑保持前一天的不持有 # 和前一天持有现在卖出时的情况for p in prices[2:]:temp = (max(dp[1][0], dp[0][1] - p), max(dp[1][1], dp[1][0] + p))dp[:] = [dp[1], temp]return max(dp[1])

714、买卖股票最佳时期含手续费

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

示例 1:

输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:  
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
注意:0 < prices.length <= 50000.    0 < prices[i] < 50000.     0 <= fee < 50000.

方法:动态规划
我们维护两个变量 \mathrm{cash}cash 和 \mathrm{hold}hold,前者表示当我们不持有股票时的最大利润,后者表示当我们持有股票时的最大利润。

在第 ii 天时,我们需要根据第 i - 1i−1 天的状态来更新 \mathrm{cash}cash 和 \mathrm{hold}hold 的值。对于 \mathrm{cash}cash,我们可以保持不变,或者将手上的股票卖出,状态转移方程为

cash = max(cash, hold + prices[i] - fee)
对于 \mathrm{hold}hold,我们可以保持不变,或者买入这一天的股票,状态转移方程为

hold = max(hold, cash - prices[i])
在计算这两个状态转移方程时,我们可以不使用临时变量来存储第 i - 1i−1 天 \mathrm{cash}cash 和 \mathrm{hold}hold 的值,而是可以先计算 \mathrm{cash}cash 再计算 \mathrm{hold}hold,原因是在同一天卖出再买入(亏了一笔手续费)一定不会比不进行任何操作好。

复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 是 \mathrm{prices}prices 数组的长度。
  • 空间复杂度:O(1)O(1)。
class Solution(object):def maxProfit(self, prices, fee):cash, hold = 0, -prices[0]for i in range(1, len(prices)):cash = max(cash, hold + prices[i] - fee)hold = max(hold, cash - prices[i])return cash

leedcode 简单一(21-40)相关推荐

  1. 使用C#实现简单的21点小游戏

    使用C#实现简单的21点小游戏 最近刚把C#学完,突发奇想就做了这么一个简易的21点小游戏 虽然说是简易,但是也碰到了一些坑,代码写的比较直接,其实可以先自定义一个放有4副扑克牌的集合类,把函数都写在 ...

  2. 软件设计师知识点100条(21~40)

    目录 21.树形目录结构(多级目录结构) 22.I/O管理软件 23.分布式透明性 24.数据库三级模式两级映像 25.数据库设计过程 26.关系模式基本概念 27.候选键 28.E-R图转关系模式转 ...

  3. C语言例题100道(21~40)

    例题21:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个.以后每天早上都吃了前一天剩下 的一半零一个.到第10天早上想再吃时 ...

  4. python比java简单好学-21、PHP和python/JAVA比,哪个更好学?

    PHP是公认比较容易学习的编程语言. 一般学习半个月左右就可以开发简单网站,一般学习四五天就可以用PHP进行网站的二次开发. 想要学习一种技能,先要知道这个技能是什么,有什么用, 尤其对于零基础又想学 ...

  5. DayDayUp之Job:牛客网—算法工程师—剑指offer之66道在线编程(解决思路及其代码)——21~40

    DayDayUp之Job:牛客网-算法工程师-剑指offer之66道在线编程(解决思路及其代码)--21~41 目录 剑指offer之66道在线编程--21~41 21.栈的压入.弹出序列 22.从上 ...

  6. [剑指offer]面试题第[25]题[Leedcode][JAVA][第21题][合并两个有序链表]

    [问题描述][简单] 将两个升序链表合并为一个新的升序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例:输入:1->2->4, 1->3->4 输出:1-&g ...

  7. 用JAVA写一个简单的21点游戏(不设置庄主的情况下)

    在某次的恶劣的JAVA上机中,菜鸟博主嗅到了一丝蔑视的味道--上个班的这个作业听说好像有点难! 怎么会! 看我! -- 好吧我看了,我好像也写不出来~ 当然事实可能是有点难度了,毕竟对于JAVA初学者 ...

  8. 21点代码python_python实现一个简单的21点游戏

    #21点扑克牌游戏python实现 #游戏者的目标是使手中的牌的点数之和不超过21点且尽量大. #计算规则是: #2至9牌,按其原点数计算,A算作1点(在我这个程序里) #10.J.Q.K牌都算作10 ...

  9. TPO68C3|Reschedule An Exam|托福听力精听|21:40~22:40

    19:40~20:30 乐词*100 20:30-21:30 不背*100 于 2023-05-28 22:43:23 首次发布 于 2023-05-30 16:30:23 更新订正 目录 听写订正​ ...

  10. leetcode python3 简单题21. Merge Two Sorted Lists(Linked)

    1.编辑器 我使用的是win10+vscode+leetcode+python3 环境配置参见我的博客: 链接 2.第二十一题 (1)题目 英文: Merge two sorted linked li ...

最新文章

  1. 输入网址按回车,到底发生了什么
  2. 将h.264视频流封装成flv格式文件(一.flv格式)
  3. 写代码有什么该注意的
  4. 橙白oj 2017级《算法分析与设计》-练习02
  5. Python连接MySQL数据库之pymysql模块使用
  6. GitHub 的 12 个实用技巧,你 get 了几个?
  7. SPLUS XML操作
  8. 4、JVM垃圾回收机制
  9. .net core | donet core IIS 文件路径问题
  10. ROS动态IP端口映射
  11. 细说ConcurrentHashMap扩容规则
  12. 接口测试工具Hoppscotch
  13. 新海诚没有参与制作的作品_还未开始!新海诚的下一部作品还是白纸
  14. #梆梆安全杯#个人对APP安全的一些看法
  15. Qt 精美的button合集
  16. Qpython读取手机短信
  17. CSS 行内元素设置高度
  18. 重磅!SK海力士600亿收购Intel存储业务!
  19. c++自制小游戏(不完美)
  20. java y z x_表示关系 X=Y=Z 的 JAVA 语言表达式为_机械工程材料及热处理答案_学小易找答案...

热门文章

  1. Linux基础——分区相关内容
  2. 本周发布大型无人船操作视频
  3. Java性能调优七种武器
  4. Windows下编译树莓派pico C\C++(Building on MS Windows)
  5. 设计模式20-中介者模式
  6. 中控CRM项目启动会议上的讲话
  7. 宝塔安装PHP占用多少内存,宝塔内存占用率高怎么办?解决办法分享一下
  8. Windows下配置Golang开发环境,并安装配置GoLand IDE
  9. 密码太多记不住怎么办
  10. “三次握手,四次挥手,Java开发人员不得不收集的代码