mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-06 23:28:29 +08:00
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
|
||||
👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)的障碍版,整体思路大体一致。
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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的后继节点呢?大家可以仔细思考一下。**
|
||||
|
||||
递归三部曲:
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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)哦。
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user