mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 16:54:50 +08:00
Merge branch 'youngyangyang04:master' into patch-4
This commit is contained in:
@ -327,6 +327,25 @@ var uniquePaths = function(m, n) {
|
||||
return dp[m - 1][n - 1]
|
||||
};
|
||||
```
|
||||
>版本二:直接将dp数值值初始化为1
|
||||
```javascript
|
||||
/**
|
||||
* @param {number} m
|
||||
* @param {number} n
|
||||
* @return {number}
|
||||
*/
|
||||
var uniquePaths = function(m, n) {
|
||||
let dp = new Array(m).fill(1).map(() => new Array(n).fill(1));
|
||||
// dp[i][j] 表示到达(i,j) 点的路径数
|
||||
for (let i=1; i<m; i++) {
|
||||
for (let j=1; j< n;j++) {
|
||||
dp[i][j]=dp[i-1][j]+dp[i][j-1];
|
||||
}
|
||||
}
|
||||
return dp[m-1][n-1];
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -88,7 +88,7 @@
|
||||
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
```
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int climbStairs(int n) {
|
||||
|
@ -125,9 +125,10 @@ public:
|
||||
|
||||
1. 明确递归函数的参数和返回值
|
||||
|
||||
参数的话为传入的节点指针,就没有其他参数需要传递了,返回值要返回传入节点为根节点树的深度。
|
||||
参数:当前传入节点。
|
||||
返回值:以当前传入节点为根节点的树的高度。
|
||||
|
||||
那么如何标记左右子树是否差值大于1呢。
|
||||
那么如何标记左右子树是否差值大于1呢?
|
||||
|
||||
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
|
||||
|
||||
@ -136,9 +137,9 @@ public:
|
||||
代码如下:
|
||||
|
||||
|
||||
```
|
||||
```CPP
|
||||
// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
|
||||
int getDepth(TreeNode* node)
|
||||
int getHeight(TreeNode* node)
|
||||
```
|
||||
|
||||
2. 明确终止条件
|
||||
@ -147,7 +148,7 @@ int getDepth(TreeNode* node)
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
@ -155,23 +156,23 @@ if (node == NULL) {
|
||||
|
||||
3. 明确单层递归的逻辑
|
||||
|
||||
如何判断当前传入节点为根节点的二叉树是否是平衡二叉树呢,当然是左子树高度和右子树高度相差。
|
||||
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
|
||||
|
||||
分别求出左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉树了。
|
||||
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。
|
||||
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
int leftDepth = depth(node->left); // 左
|
||||
if (leftDepth == -1) return -1;
|
||||
int rightDepth = depth(node->right); // 右
|
||||
if (rightDepth == -1) return -1;
|
||||
int leftHeight = getHeight(node->left); // 左
|
||||
if (leftHeight == -1) return -1;
|
||||
int rightHeight = getHeight(node->right); // 右
|
||||
if (rightHeight == -1) return -1;
|
||||
|
||||
int result;
|
||||
if (abs(leftDepth - rightDepth) > 1) { // 中
|
||||
if (abs(leftHeight - rightHeight) > 1) { // 中
|
||||
result = -1;
|
||||
} else {
|
||||
result = 1 + max(leftDepth, rightDepth); // 以当前节点为根节点的最大高度
|
||||
result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -180,27 +181,27 @@ return result;
|
||||
代码精简之后如下:
|
||||
|
||||
```CPP
|
||||
int leftDepth = getDepth(node->left);
|
||||
if (leftDepth == -1) return -1;
|
||||
int rightDepth = getDepth(node->right);
|
||||
if (rightDepth == -1) return -1;
|
||||
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
|
||||
int leftHeight = getHeight(node->left);
|
||||
if (leftHeight == -1) return -1;
|
||||
int rightHeight = getHeight(node->right);
|
||||
if (rightHeight == -1) return -1;
|
||||
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
|
||||
```
|
||||
|
||||
此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树,则返回-1。
|
||||
|
||||
getDepth整体代码如下:
|
||||
getHeight整体代码如下:
|
||||
|
||||
```CPP
|
||||
int getDepth(TreeNode* node) {
|
||||
int getHeight(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int leftDepth = getDepth(node->left);
|
||||
if (leftDepth == -1) return -1;
|
||||
int rightDepth = getDepth(node->right);
|
||||
if (rightDepth == -1) return -1;
|
||||
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
|
||||
int leftHeight = getHeight(node->left);
|
||||
if (leftHeight == -1) return -1;
|
||||
int rightHeight = getHeight(node->right);
|
||||
if (rightHeight == -1) return -1;
|
||||
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
|
||||
}
|
||||
```
|
||||
|
||||
@ -210,18 +211,18 @@ int getDepth(TreeNode* node) {
|
||||
class Solution {
|
||||
public:
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
int getDepth(TreeNode* node) {
|
||||
int getHeight(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int leftDepth = getDepth(node->left);
|
||||
if (leftDepth == -1) return -1; // 说明左子树已经不是二叉平衡树
|
||||
int rightDepth = getDepth(node->right);
|
||||
if (rightDepth == -1) return -1; // 说明右子树已经不是二叉平衡树
|
||||
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
|
||||
int leftHeight = getHeight(node->left);
|
||||
if (leftHeight == -1) return -1;
|
||||
int rightHeight = getHeight(node->right);
|
||||
if (rightHeight == -1) return -1;
|
||||
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
|
||||
}
|
||||
bool isBalanced(TreeNode* root) {
|
||||
return getDepth(root) == -1 ? false : true;
|
||||
return getHeight(root) == -1 ? false : true;
|
||||
}
|
||||
};
|
||||
```
|
||||
@ -498,20 +499,35 @@ class Solution {
|
||||
## Python
|
||||
|
||||
递归法:
|
||||
```python
|
||||
```python3
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def isBalanced(self, root: TreeNode) -> bool:
|
||||
return True if self.getDepth(root) != -1 else False
|
||||
if self.get_height(root) != -1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
def getDepth(self, node):
|
||||
if not node:
|
||||
def get_height(self, root: TreeNode) -> int:
|
||||
# Base Case
|
||||
if not root:
|
||||
return 0
|
||||
leftDepth = self.getDepth(node.left)
|
||||
if leftDepth == -1: return -1 #说明左子树已经不是二叉平衡树
|
||||
rightDepth = self.getDepth(node.right)
|
||||
if rightDepth == -1: return -1 #说明右子树已经不是二叉平衡树
|
||||
return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth)
|
||||
# 左
|
||||
if (left_height := self.get_height(root.left)) == -1:
|
||||
return -1
|
||||
# 右
|
||||
if (right_height := self.get_height(root.right)) == -1:
|
||||
return -1
|
||||
# 中
|
||||
if abs(left_height - right_height) > 1:
|
||||
return -1
|
||||
else:
|
||||
return 1 + max(left_height, right_height)
|
||||
```
|
||||
|
||||
迭代法:
|
||||
|
@ -96,6 +96,28 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。
|
||||
|
||||
具体代码如下(带详细注释):
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* reverseList(ListNode* head) {
|
||||
// 边缘条件判断
|
||||
if(head == NULL) return NULL;
|
||||
if (head->next == NULL) return head;
|
||||
|
||||
// 递归调用,翻转第二个节点开始往后的链表
|
||||
ListNode *last = reverseList(head->next);
|
||||
// 翻转头节点与第二个节点的指向
|
||||
head->next->next = head;
|
||||
// 此时的 head 节点为尾节点,next 需要指向 NULL
|
||||
head->next = NULL;
|
||||
return last;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
@ -135,9 +157,9 @@ class Solution {
|
||||
temp = cur.next;// 先保存下一个节点
|
||||
cur.next = prev;// 反转
|
||||
// 更新prev、cur位置
|
||||
prev = cur;
|
||||
cur = temp;
|
||||
return reverse(prev, cur);
|
||||
// prev = cur;
|
||||
// cur = temp;
|
||||
return reverse(cur, temp);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -404,33 +404,41 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
```Python
|
||||
---
|
||||
Python:
|
||||
递归法+隐形回溯
|
||||
```Python3
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
"""二叉树的所有路径 递归法"""
|
||||
|
||||
def binaryTreePaths(self, root: TreeNode) -> List[str]:
|
||||
path, result = '', []
|
||||
path = ''
|
||||
result = []
|
||||
if not root: return result
|
||||
self.traversal(root, path, result)
|
||||
return result
|
||||
|
||||
def traversal(self, cur: TreeNode, path: List, result: List):
|
||||
def traversal(self, cur: TreeNode, path: str, result: List[str]) -> None:
|
||||
path += str(cur.val)
|
||||
# 如果当前节点为叶子节点,添加路径到结果中
|
||||
if not (cur.left or cur.right):
|
||||
# 若当前节点为leave,直接输出
|
||||
if not cur.left and not cur.right:
|
||||
result.append(path)
|
||||
return
|
||||
|
||||
if cur.left:
|
||||
# + '->' 是隐藏回溯
|
||||
self.traversal(cur.left, path + '->', result)
|
||||
|
||||
if cur.right:
|
||||
self.traversal(cur.right, path + '->', result)
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
迭代法:
|
||||
|
||||
```python3
|
||||
from collections import deque
|
||||
|
||||
|
||||
@ -458,6 +466,7 @@ class Solution:
|
||||
return result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Go:
|
||||
```go
|
||||
@ -482,7 +491,7 @@ func binaryTreePaths(root *TreeNode) []string {
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
JavaScript:
|
||||
|
||||
1.递归版本
|
||||
|
@ -334,8 +334,8 @@ var numSquares1 = function(n) {
|
||||
let dp = new Array(n + 1).fill(Infinity)
|
||||
dp[0] = 0
|
||||
|
||||
for(let i = 0; i <= n; i++) {
|
||||
let val = i * i
|
||||
for(let i = 1; i**2 <= n; i++) {
|
||||
let val = i**2
|
||||
for(let j = val; j <= n; j++) {
|
||||
dp[j] = Math.min(dp[j], dp[j - val] + 1)
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```
|
||||
```java
|
||||
class Solution {
|
||||
public boolean isSubsequence(String s, String t) {
|
||||
int length1 = s.length(); int length2 = t.length();
|
||||
|
@ -227,7 +227,7 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
```
|
||||
```go
|
||||
func canPartition(nums []int) bool {
|
||||
/**
|
||||
动态五部曲:
|
||||
|
@ -371,7 +371,6 @@ const findTargetSumWays = (nums, target) => {
|
||||
}
|
||||
|
||||
const halfSum = (target + sum) / 2;
|
||||
nums.sort((a, b) => a - b);
|
||||
|
||||
let dp = new Array(halfSum+1).fill(0);
|
||||
dp[0] = 1;
|
||||
|
@ -262,6 +262,34 @@ var maxProfit = function(prices, fee) {
|
||||
}
|
||||
return result
|
||||
};
|
||||
|
||||
// 动态规划
|
||||
/**
|
||||
* @param {number[]} prices
|
||||
* @param {number} fee
|
||||
* @return {number}
|
||||
*/
|
||||
var maxProfit = function(prices, fee) {
|
||||
// 滚动数组
|
||||
// have表示当天持有股票的最大收益
|
||||
// notHave表示当天不持有股票的最大收益
|
||||
// 把手续费算在买入价格中
|
||||
let n = prices.length,
|
||||
have = -prices[0]-fee, // 第0天持有股票的最大收益
|
||||
notHave = 0; // 第0天不持有股票的最大收益
|
||||
for (let i = 1; i < n; i++) {
|
||||
// 第i天持有股票的最大收益由两种情况组成
|
||||
// 1、第i-1天就已经持有股票,第i天什么也没做
|
||||
// 2、第i-1天不持有股票,第i天刚买入
|
||||
have = Math.max(have, notHave - prices[i] - fee);
|
||||
// 第i天不持有股票的最大收益由两种情况组成
|
||||
// 1、第i-1天就已经不持有股票,第i天什么也没做
|
||||
// 2、第i-1天持有股票,第i天刚卖出
|
||||
notHave = Math.max(notHave, have + prices[i]);
|
||||
}
|
||||
// 最后手中不持有股票,收益才能最大化
|
||||
return notHave;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -127,6 +127,7 @@ public:
|
||||
|
||||
Java:
|
||||
```java
|
||||
版本1
|
||||
class Solution {
|
||||
public int monotoneIncreasingDigits(int N) {
|
||||
String[] strings = (N + "").split("");
|
||||
@ -144,6 +145,31 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
java版本1中创建了String数组,多次使用Integer.parseInt了方法,这导致不管是耗时还是空间占用都非常高,用时12ms,下面提供一个版本在char数组上原地修改,用时1ms的版本
|
||||
```java
|
||||
版本2
|
||||
class Solution {
|
||||
public int monotoneIncreasingDigits(int n) {
|
||||
if (n==0)return 0;
|
||||
char[] chars= Integer.toString(n).toCharArray();
|
||||
int start=Integer.MAX_VALUE;//start初始值设为最大值,这是为了防止当数字本身是单调递增时,没有一位数字需要改成9的情况
|
||||
for (int i=chars.length-1;i>0;i--){
|
||||
if (chars[i]<chars[i-1]){
|
||||
chars[i-1]--;
|
||||
start=i;
|
||||
}
|
||||
}
|
||||
StringBuilder res=new StringBuilder();
|
||||
for (int i=0;i<chars.length;i++){
|
||||
if (chars[i]=='0'&&i==0)continue;//防止出现09这样的情况
|
||||
if (i>=start){
|
||||
res.append('9');
|
||||
}else res.append(chars[i]);
|
||||
}
|
||||
return Integer.parseInt(res.toString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Python:
|
||||
|
@ -145,21 +145,21 @@ if (cur->right) {
|
||||
}
|
||||
```
|
||||
|
||||
此时就没有回溯了,这个代码就是通过不了的了。
|
||||
因为在递归右子树之前需要还原path,所以在左子树递归后必须为了右子树而进行回溯操作。而只右子树自己不添加回溯也可以成功AC。
|
||||
|
||||
如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。
|
||||
因此,在上面代码的基础上,再加上左右子树的回溯代码,就可以AC了。
|
||||
|
||||
```CPP
|
||||
if (cur->left) {
|
||||
path += "->";
|
||||
traversal(cur->left, path, result); // 左
|
||||
path.pop_back(); // 回溯
|
||||
path.pop_back();
|
||||
path.pop_back(); // 回溯,抛掉val
|
||||
path.pop_back(); // 回溯,抛掉->
|
||||
}
|
||||
if (cur->right) {
|
||||
path += "->";
|
||||
traversal(cur->right, path, result); // 右
|
||||
path.pop_back(); // 回溯
|
||||
path.pop_back(); // 回溯(非必要)
|
||||
path.pop_back();
|
||||
}
|
||||
```
|
||||
|
Reference in New Issue
Block a user