Merge branch 'master' of github.com:youngyangyang04/leetcode-master

This commit is contained in:
youngyangyang04
2022-03-31 15:09:26 +08:00
13 changed files with 266 additions and 75 deletions

View File

@ -1,5 +1,4 @@
👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定) 👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master) 👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)

View File

@ -254,32 +254,20 @@ TypeScript
```typescript ```typescript
function swapPairs(head: ListNode | null): ListNode | null { function swapPairs(head: ListNode | null): ListNode | null {
/** const dummyHead: ListNode = new ListNode(0, head);
* 初始状态: let cur: ListNode = dummyHead;
* curNode -> node1 -> node2 -> tmepNode while(cur.next !== null && cur.next.next !== null) {
* 转换过程: const tem: ListNode = cur.next;
* curNode -> node2 const tem1: ListNode = cur.next.next.next;
* curNode -> node2 -> node1
* curNode -> node2 -> node1 -> tempNode cur.next = cur.next.next; // step 1
* curNode = node1 cur.next.next = tem; // step 2
*/ cur.next.next.next = tem1; // step 3
let retNode: ListNode | null = new ListNode(0, head),
curNode: ListNode | null = retNode, cur = cur.next.next;
node1: ListNode | null = null, }
node2: ListNode | null = null, return dummyHead.next;
tempNode: ListNode | null = null; }
while (curNode && curNode.next && curNode.next.next) {
node1 = curNode.next;
node2 = curNode.next.next;
tempNode = node2.next;
curNode.next = node2;
node2.next = node1;
node1.next = tempNode;
curNode = node1;
}
return retNode.next;
};
``` ```
Kotlin: Kotlin:

View File

@ -155,6 +155,39 @@ public:
* 时间复杂度O(n × m)n、m 分别为obstacleGrid 长度和宽度 * 时间复杂度O(n × m)n、m 分别为obstacleGrid 长度和宽度
* 空间复杂度O(n × m) * 空间复杂度O(n × m)
同样我们给出空间优化版本:
```CPP
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
if (obstacleGrid[0][0] == 1)
return 0;
vector<int> dp(obstacleGrid[0].size());
for (int j = 0; j < dp.size(); ++j)
if (obstacleGrid[0][j] == 1)
dp[j] = 0;
else if (j == 0)
dp[j] = 1;
else
dp[j] = dp[j-1];
for (int i = 1; i < obstacleGrid.size(); ++i)
for (int j = 0; j < dp.size(); ++j){
if (obstacleGrid[i][j] == 1)
dp[j] = 0;
else if (j != 0)
dp[j] = dp[j] + dp[j-1];
}
return dp.back();
}
};
```
* 时间复杂度:$O(n × m)$n、m 分别为obstacleGrid 长度和宽度
* 空间复杂度:$O(m)$
## 总结 ## 总结
本题是[62.不同路径](https://programmercarl.com/0062.不同路径.html)的障碍版,整体思路大体一致。 本题是[62.不同路径](https://programmercarl.com/0062.不同路径.html)的障碍版,整体思路大体一致。

View File

@ -1287,23 +1287,23 @@ java代码
```java ```java
class Solution { class Solution {
public List<Integer> largestValues(TreeNode root) { public List<Integer> largestValues(TreeNode root) {
List<Integer> retVal = new ArrayList<Integer>(); if(root == null){
Queue<TreeNode> tmpQueue = new LinkedList<TreeNode>(); return Collections.emptyList();
if (root != null) tmpQueue.add(root); }
List<Integer> result = new ArrayList();
while (tmpQueue.size() != 0){ Queue<TreeNode> queue = new LinkedList();
int size = tmpQueue.size(); queue.offer(root);
List<Integer> lvlVals = new ArrayList<Integer>(); while(!queue.isEmpty()){
for (int index = 0; index < size; index++){ int max = Integer.MIN_VALUE;
TreeNode node = tmpQueue.poll(); for(int i = queue.size(); i > 0; i--){
lvlVals.add(node.val); TreeNode node = queue.poll();
if (node.left != null) tmpQueue.add(node.left); max = Math.max(max, node.val);
if (node.right != null) tmpQueue.add(node.right); if(node.left != null) queue.offer(node.left);
} if(node.right != null) queue.offer(node.right);
retVal.add(Collections.max(lvlVals)); }
} result.add(max);
}
return retVal; return result;
} }
} }
``` ```

View File

@ -250,30 +250,34 @@ class Solution {
// 回溯法+记忆化 // 回溯法+记忆化
class Solution { class Solution {
private Set<String> set;
private int[] memo;
public boolean wordBreak(String s, List<String> wordDict) { public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet = new HashSet(wordDict); memo = new int[s.length()];
int[] memory = new int[s.length()]; set = new HashSet<>(wordDict);
return backTrack(s, wordDictSet, 0, memory); return backtracking(s, 0);
} }
public boolean backTrack(String s, Set<String> wordDictSet, int startIndex, int[] memory) { public boolean backtracking(String s, int startIndex) {
// 结束条件 // System.out.println(startIndex);
if (startIndex >= s.length()) { if (startIndex == s.length()) {
return true; return true;
} }
if (memory[startIndex] != 0) { if (memo[startIndex] == -1) {
// 此处认为memory[i] = 1 表示可以拼出i 及以后的字符子串, memory[i] = -1 表示不能 return false;
return memory[startIndex] == 1 ? true : false;
} }
for (int i = startIndex; i < s.length(); ++i) {
// 处理 递归 回溯 循环不变量:[startIndex, i + 1) for (int i = startIndex; i < s.length(); i++) {
String word = s.substring(startIndex, i + 1); String sub = s.substring(startIndex, i + 1);
if (wordDictSet.contains(word) && backTrack(s, wordDictSet, i + 1, memory)) { // 拆分出来的单词无法匹配
memory[startIndex] = 1; if (!set.contains(sub)) {
return true; continue;
} }
boolean res = backtracking(s, i + 1);
if (res) return true;
} }
memory[startIndex] = -1; // 这里是关键找遍了startIndex~s.length()也没能完全匹配标记从startIndex开始不能找到
memo[startIndex] = -1;
return false; return false;
} }
} }

View File

@ -324,7 +324,7 @@ function removeElements(head: ListNode | null, val: number): ListNode | null {
head = head.next; head = head.next;
} }
if (head === null) return head; if (head === null) return head;
let pre: ListNode = head, cur: ListNode = head.next; let pre: ListNode = head, cur: ListNode | null = head.next;
// 删除非头部节点 // 删除非头部节点
while (cur) { while (cur) {
if (cur.val === val) { if (cur.val === val) {
@ -342,14 +342,14 @@ function removeElements(head: ListNode | null, val: number): ListNode | null {
```typescript ```typescript
function removeElements(head: ListNode | null, val: number): ListNode | null { function removeElements(head: ListNode | null, val: number): ListNode | null {
head = new ListNode(0, head); let dummyHead = new ListNode(0, head);
let pre: ListNode = head, cur: ListNode = head.next; let pre: ListNode = dummyHead, cur: ListNode | null = dummyHead.next;
// 删除非头部节点 // 删除非头部节点
while (cur) { while (cur) {
if (cur.val === val) { if (cur.val === val) {
pre.next = cur.next; pre.next = cur.next;
} else { } else {
pre = pre.next; pre = cur;
} }
cur = cur.next; cur = cur.next;
} }

View File

@ -45,9 +45,13 @@
接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。 接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。
**如果找到一个节点发现左子树出现结点p右子树出现节点q或者 左子树出现结点q右子树出现节点p那么该节点就是节点p和q的最近公共祖先。** **首先最容易想到的一个情况:如果找到一个节点发现左子树出现结点p右子树出现节点q或者 左子树出现结点q右子树出现节点p那么该节点就是节点p和q的最近公共祖先。**
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现如何这个条件的节点,就是最近公共节点了。 **但是很多人容易忽略一个情况就是节点本身p(q)它拥有一个子孙节点q(p)。**
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现满足第一种情况的节点,就是最近公共节点了。
**但是如果p或者q本身就是最近公共祖先呢其实只需要找到一个节点是p或者q的时候直接返回当前节点无需继续递归子树。如果接下来的遍历中找到了后继节点满足第一种情况则修改返回值为后继节点否则继续返回已找到的节点即可。为什么满足第一种情况的节点一定是p或q的后继节点呢?大家可以仔细思考一下。**
递归三部曲: 递归三部曲:

View File

@ -205,6 +205,29 @@ class Solution {
} }
} }
``` ```
```java
//另一种解题思路
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length + 1][2];
dp[1][0] = -prices[0];
for (int i = 2; i <= prices.length; i++) {
/*
dp[i][0] 第i天未持有股票收益;
dp[i][1] 第i天持有股票收益;
情况一第i天是冷静期不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题
情况二第i天不是冷静期理论上应该以dp[i-1][1]购买股票但是第i天不是冷静期说明第i-1天没有卖出股票
则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题
*/
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 2][1] - prices[i - 1]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i - 1]);
}
return dp[prices.length][1];
}
}
```
Python Python

View File

@ -251,14 +251,14 @@ Python
```python ```python
class Solution: class Solution:
def canPartition(self, nums: List[int]) -> bool: def canPartition(self, nums: List[int]) -> bool:
taraget = sum(nums) target = sum(nums)
if taraget % 2 == 1: return False if target % 2 == 1: return False
taraget //= 2 target //= 2
dp = [0] * 10001 dp = [0] * 10001
for i in range(len(nums)): for i in range(len(nums)):
for j in range(taraget, nums[i] - 1, -1): for j in range(target, nums[i] - 1, -1):
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return taraget == dp[taraget] return target == dp[target]
``` ```
Go Go
```go ```go

View File

@ -518,6 +518,70 @@ var deleteNode = function (root, key) {
} }
``` ```
## TypeScript
> 递归法:
```typescript
function deleteNode(root: TreeNode | null, key: number): TreeNode | null {
if (root === null) return null;
if (root.val === key) {
if (root.left === null && root.right === null) return null;
if (root.left === null) return root.right;
if (root.right === null) return root.left;
let curNode: TreeNode = root.right;
while (curNode.left !== null) {
curNode = curNode.left;
}
curNode.left = root.left;
return root.right;
}
if (root.val > key) root.left = deleteNode(root.left, key);
if (root.val < key) root.right = deleteNode(root.right, key);
return root;
};
```
> 迭代法:
```typescript
function deleteNode(root: TreeNode | null, key: number): TreeNode | null {
function removeTargetNode(root: TreeNode): TreeNode | null {
if (root.left === null && root.right === null) return null;
if (root.right === null) return root.left;
if (root.left === null) return root.right;
let curNode: TreeNode | null = root.right;
while (curNode.left !== null) {
curNode = curNode.left;
}
curNode.left = root.left;
return root.right;
}
let preNode: TreeNode | null = null,
curNode: TreeNode | null = root;
while (curNode !== null) {
if (curNode.val === key) break;
preNode = curNode;
if (curNode.val > key) {
curNode = curNode.left;
} else {
curNode = curNode.right;
}
}
if (curNode === null) return root;
if (preNode === null) {
// 删除头节点
return removeTargetNode(curNode);
}
if (preNode.val > key) {
preNode.left = removeTargetNode(curNode);
} else {
preNode.right = removeTargetNode(curNode);
}
return root;
};
```
----------------------- -----------------------

View File

@ -280,7 +280,7 @@ class Solution:
# 返回更新后的以当前root为根节点的新树 # 返回更新后的以当前root为根节点的新树
return roo return roo
``` ```
**递归法** - 无返回值 **递归法** - 无返回值
```python ```python
@ -308,7 +308,7 @@ class Solution:
return return
__traverse(root, val) __traverse(root, val)
return root return root
``` ```
**递归法** - 无返回值 - another easier way **递归法** - 无返回值 - another easier way
```python ```python
@ -378,7 +378,7 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode {
} }
return root return root
} }
``` ```
迭代法 迭代法
@ -520,5 +520,71 @@ var insertIntoBST = function (root, val) {
}; };
``` ```
## TypeScript
> 递归-有返回值
```typescript
function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null {
if (root === null) return new TreeNode(val);
if (root.val > val) {
root.left = insertIntoBST(root.left, val);
} else {
root.right = insertIntoBST(root.right, val);
}
return root;
};
```
> 递归-无返回值
```typescript
function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null {
if (root === null) return new TreeNode(val);
function recur(root: TreeNode | null, val: number) {
if (root === null) {
if (parentNode.val > val) {
parentNode.left = new TreeNode(val);
} else {
parentNode.right = new TreeNode(val);
}
return;
}
parentNode = root;
if (root.val > val) recur(root.left, val);
if (root.val < val) recur(root.right, val);
}
let parentNode: TreeNode = root;
recur(root, val);
return root;
};
```
> 迭代法
```typescript
function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null {
if (root === null) return new TreeNode(val);
let curNode: TreeNode | null = root;
let parentNode: TreeNode = root;
while (curNode !== null) {
parentNode = curNode;
if (curNode.val > val) {
curNode = curNode.left
} else {
curNode = curNode.right;
}
}
if (parentNode.val > val) {
parentNode.left = new TreeNode(val);
} else {
parentNode.right = new TreeNode(val);
}
return root;
};
```
----------------------- -----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div> <div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -253,6 +253,7 @@ var commonChars = function (words) {
return res return res
}; };
``` ```
TypeScript TypeScript
```ts ```ts
console.time("test") console.time("test")
@ -288,6 +289,7 @@ TypeScript
console.timeEnd("test") console.timeEnd("test")
return str.split("") return str.split("")
``` ```
GO GO
```golang ```golang
func commonChars(words []string) []string { func commonChars(words []string) []string {

View File

@ -171,6 +171,14 @@ Python
Go Go
Go中slice的`append`操作和C++中vector的扩容机制基本相同。
说是基本呢,其实是因为大家平时刷题和工作中遇到的数据不会特别大。
具体来说当当前slice的长度小于**1024**时,执行`append`操作新slice的capacity会变成当前的2倍而当slice长度大于等于**1024**时slice的扩容变成了每次增加当前slice长度的**1/4**。
在Go Slice的底层实现中如果capacity不够时会做一个reslice的操作底层数组也会重新被复制到另一块内存区域中所以`append`一个元素不一定是O(1), 也可能是O(n)哦。