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在国内访问经常不稳定)
👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)

View File

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

View File

@ -155,6 +155,39 @@ public:
* 时间复杂度O(n × m)n、m 分别为obstacleGrid 长度和宽度
* 空间复杂度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)的障碍版,整体思路大体一致。

View File

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

View File

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

View File

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

View File

@ -45,9 +45,13 @@
接下来就看如何判断一个节点是节点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

View File

@ -251,14 +251,14 @@ Python
```python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
taraget = sum(nums)
if taraget % 2 == 1: return False
taraget //= 2
target = sum(nums)
if target % 2 == 1: return False
target //= 2
dp = [0] * 10001
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])
return taraget == dp[taraget]
return target == dp[target]
```
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

@ -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>

View File

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

View File

@ -171,6 +171,14 @@ Python
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)哦。