Merge branch 'master' into remote

This commit is contained in:
程序员Carl
2021-09-04 16:59:48 +08:00
committed by GitHub
54 changed files with 1358 additions and 289 deletions

View File

@ -1,4 +1,4 @@
👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)
> 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者)
@ -494,6 +494,7 @@
## 图论
* [463.岛屿的周长](./problems/0463.岛屿的周长.md) (模拟)
* [841.钥匙和房间](./problems/0841.钥匙和房间.md) 【有向图】dfsbfs都可以
* [127.单词接龙](./problems/0127.单词接龙.md) 广搜
## 并查集
* [684.冗余连接](./problems/0684.冗余连接.md) 【并查集基础题目】

View File

@ -206,6 +206,23 @@ function twoSum(array $nums, int $target): array
}
```
Swift
```swift
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var res = [Int]()
var dict = [Int : Int]()
for i in 0 ..< nums.count {
let other = target - nums[i]
if dict.keys.contains(other) {
res.append(i)
res.append(dict[other]!)
return res
}
dict[nums[i]] = i
}
return res
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -246,6 +246,30 @@ func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
}
```
PHP:
```php
class Solution {
/**
* @param Integer[] $nums
* @param Integer $val
* @return Integer
*/
function removeElement(&$nums, $val) {
if (count($nums) == 0) {
return 0;
}
// 快慢指针
$slow = 0;
for ($fast = 0; $fast < count($nums); $fast++) {
if ($nums[$fast] != $val) {
$nums[$slow] = $nums[$fast];
$slow++;
}
}
return $slow;
}
```
C:
```c
int removeElement(int* nums, int numsSize, int val){

View File

@ -183,6 +183,32 @@ class Solution {
}
}
```
```java
// 解法2通过判断path中是否存在数字排除已经选择的数字
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0) return result;
backtrack(nums, path);
return result;
}
public void backtrack(int[] nums, LinkedList<Integer> path) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
}
for (int i =0; i < nums.length; i++) {
// 如果path中已有则跳过
if (path.contains(nums[i])) {
continue;
}
path.add(nums[i]);
backtrack(nums, path);
path.removeLast();
}
}
}
```
Python
```python3

View File

@ -157,6 +157,28 @@ class Solution {
}
}
```
```java
// 版本2
class Solution {
public int[][] merge(int[][] intervals) {
LinkedList<int[]> res = new LinkedList<>();
Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));
res.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] <= res.getLast()[1]) {
int start = res.getLast()[0];
int end = Math.max(intervals[i][1], res.getLast()[1]);
res.removeLast();
res.add(new int[]{start, end});
}
else {
res.add(intervals[i]);
}
}
return res.toArray(new int[res.size()][]);
}
}
```
Python
```python

View File

@ -191,33 +191,48 @@ class Solution {
python:
```python
```python3
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
left, right, up, down = 0, n-1, 0, n-1
matrix = [ [0]*n for _ in range(n)]
num = 1
while left<=right and up<=down:
# 填充左到右
for i in range(left, right+1):
matrix[up][i] = num
num += 1
up += 1
# 填充上到下
for i in range(up, down+1):
matrix[i][right] = num
num += 1
right -= 1
# 填充右到左
for i in range(right, left-1, -1):
matrix[down][i] = num
num += 1
down -= 1
# 填充下到上
for i in range(down, up-1, -1):
matrix[i][left] = num
num += 1
# 初始化要填充的正方形
matrix = [[0] * n for _ in range(n)]
left, right, up, down = 0, n - 1, 0, n - 1
number = 1 # 填充的数字
while left < right and up < down:
# 从左到右填充上边
for x in range(left, right):
matrix[up][x] = number
number += 1
# 从上到下填充右边
for y in range(up, down):
matrix[y][right] = number
number += 1
# 从右到左填充下边
for x in range(right, left, -1):
matrix[down][x] = number
number += 1
# 从下到上填充左边
for y in range(down, up, -1):
matrix[y][left] = number
number += 1
# 缩小要填充的范围
left += 1
right -= 1
up += 1
down -= 1
# 如果阶数为奇数,额外填充一次中心
if n % 2:
matrix[n // 2][n // 2] = number
return matrix
```
@ -358,6 +373,100 @@ func generateMatrix(_ n: Int) -> [[Int]] {
}
```
Rust:
```rust
impl Solution {
pub fn generate_matrix(n: i32) -> Vec<Vec<i32>> {
let mut res = vec![vec![0; n as usize]; n as usize];
let (mut startX, mut startY, mut offset): (usize, usize, usize) = (0, 0, 1);
let mut loopIdx = n/2;
let mid: usize = loopIdx as usize;
let mut count = 1;
let (mut i, mut j): (usize, usize) = (0, 0);
while loopIdx > 0 {
i = startX;
j = startY;
while j < (startY + (n as usize) - offset) {
res[i][j] = count;
count += 1;
j += 1;
}
while i < (startX + (n as usize) - offset) {
res[i][j] = count;
count += 1;
i += 1;
}
while j > startY {
res[i][j] = count;
count += 1;
j -= 1;
}
while i > startX {
res[i][j] = count;
count += 1;
i -= 1;
}
startX += 1;
startY += 1;
offset += 2;
loopIdx -= 1;
}
if(n % 2 == 1) {
res[mid][mid] = count;
}
res
}
}
```
PHP:
```php
class Solution {
/**
* @param Integer $n
* @return Integer[][]
*/
function generateMatrix($n) {
// 初始化数组
$res = array_fill(0, $n, array_fill(0, $n, 0));
$mid = $loop = floor($n / 2);
$startX = $startY = 0;
$offset = 1;
$count = 1;
while ($loop > 0) {
$i = $startX;
$j = $startY;
for (; $j < $startY + $n - $offset; $j++) {
$res[$i][$j] = $count++;
}
for (; $i < $startX + $n - $offset; $i++) {
$res[$i][$j] = $count++;
}
for (; $j > $startY; $j--) {
$res[$i][$j] = $count++;
}
for (; $i > $startX; $i--) {
$res[$i][$j] = $count++;
}
$startX += 1;
$startY += 1;
$offset += 2;
$loop--;
}
if ($n % 2 == 1) {
$res[$mid][$mid] = $count;
}
return $res;
}
}
```
-----------------------

View File

@ -221,6 +221,30 @@ class SolutionDP2:
```
Go
```go
func numDistinct(s string, t string) int {
dp:= make([][]int,len(s)+1)
for i:=0;i<len(dp);i++{
dp[i] = make([]int,len(t)+1)
}
// 初始化
for i:=0;i<len(dp);i++{
dp[i][0] = 1
}
// dp[0][j] 为 0默认值因此不需要初始化
for i:=1;i<len(dp);i++{
for j:=1;j<len(dp[i]);j++{
if s[i-1] == t[j-1]{
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
}else{
dp[i][j] = dp[i-1][j]
}
}
}
return dp[len(dp)-1][len(dp[0])-1]
}
```
Javascript:
```javascript

View File

@ -0,0 +1,150 @@
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 127. 单词接龙
[力扣题目链接](https://leetcode-cn.com/problems/word-ladder/)
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:
* 序列中第一个单词是 beginWord 。
* 序列中最后一个单词是 endWord 。
* 每次转换只能改变一个字母。
* 转换过程中的中间单词必须是字典 wordList 中的单词。
* 给你两个单词 beginWord 和 endWord 和一个字典 wordList 找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。
 
示例 1
* 输入beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
* 输出5
* 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
示例 2
* 输入beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
* 输出0
* 解释endWord "cog" 不在字典中,所以无法进行转换。
# 思路
以示例1为例从这个图中可以看出 hit 到 cog的路线不止一条有三条两条是最短的长度为5一条长度为6。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210827175432.png)
本题只需要求出最短长度就可以了,不用找出路径。
所以这道题要解决两个问题:
* 图中的线是如何连在一起的
* 起点和终点的最短路径长度
首先题目中并没有给出点与点之间的连线,而是要我们自己去连,条件是字符只能差一个,所以判断点与点之间的关系,要自己判断是不是差一个字符,如果差一个字符,那就是有链接。
然后就是求起点和终点的最短路径长度,**这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径**。因为广搜就是以起点中心向四周扩散的搜索。
本题如果用深搜,会非常麻烦。
另外需要有一个注意点:
* 本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环!
* 本题给出集合是数组型的可以转成set结构查找更快一些
C++代码如下:(详细注释)
```CPP
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
// 将vector转成unordered_set提高查询速度
unordered_set<string> wordSet(wordList.begin(), wordList.end());
// 如果endWord没有在wordSet出现直接返回0
if (wordSet.find(endWord) == wordSet.end()) return 0;
// 记录word是否访问过
unordered_map<string, int> visitMap; // <word, 查询到这个word路径长度>
// 初始化队列
queue<string> que;
que.push(beginWord);
// 初始化visitMap
visitMap.insert(pair<string, int>(beginWord, 1));
while(!que.empty()) {
string word = que.front();
que.pop();
int path = visitMap[word]; // 这个word的路径长度
for (int i = 0; i < word.size(); i++) {
string newWord = word; // 用一个新单词替换word因为每次置换一个字母
for (int j = 0 ; j < 26; j++) {
newWord[i] = j + 'a';
if (newWord == endWord) return path + 1; // 找到了end返回path+1
// wordSet出现了newWord并且newWord没有被访问过
if (wordSet.find(newWord) != wordSet.end()
&& visitMap.find(newWord) == visitMap.end()) {
// 添加访问信息
visitMap.insert(pair<string, int>(newWord, path + 1));
que.push(newWord);
}
}
}
}
return 0;
}
};
```
# 其他语言版本
## Java
```java
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashSet<String> wordSet = new HashSet<>(wordList); //转换为hashset 加快速度
if (wordSet.size() == 0 || !wordSet.contains(endWord)) { //特殊情况判断
return 0;
}
Queue<String> queue = new LinkedList<>(); //bfs 队列
queue.offer(beginWord);
Map<String, Integer> map = new HashMap<>(); //记录单词对应路径长度
map.put(beginWord, 1);
while (!queue.isEmpty()) {
String word = queue.poll(); //取出队头单词
int path = map.get(word); //获取到该单词的路径长度
for (int i = 0; i < word.length(); i++) { //遍历单词的每个字符
char[] chars = word.toCharArray(); //将单词转换为char array方便替换
for (char k = 'a'; k <= 'z'; k++) { //从'a' 到 'z' 遍历替换
chars[i] = k; //替换第i个字符
String newWord = String.valueOf(chars); //得到新的字符串
if (newWord.equals(endWord)) { //如果新的字符串值与endWord一致返回当前长度+1
return path + 1;
}
if (wordSet.contains(newWord) && !map.containsKey(newWord)) { //如果新单词在set中但是没有访问过
map.put(newWord, path + 1); //记录单词对应的路径长度
queue.offer(newWord);//加入队尾
}
}
}
}
return 0; //未找到
}
```
## Python
## Go
## JavaScript
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>

View File

@ -200,6 +200,7 @@ public:
Java
```java
// 解法1
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int sum = 0;
@ -221,7 +222,26 @@ class Solution {
}
}
```
```java
// 解法2
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curSum = 0;
int totalSum = 0;
int index = 0;
for (int i = 0; i < gas.length; i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) {
index = (i + 1) % gas.length ;
curSum = 0;
}
}
if (totalSum < 0) return -1;
return index;
}
}
```
Python
```python
class Solution:

View File

@ -13,7 +13,7 @@
# 150. 逆波兰表达式求值
https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
[力扣题目链接](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
根据 逆波兰表示法,求表达式的值。
@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
 
示例 1
* 输入: ["2", "1", "+", "3", " * "]
@ -37,16 +37,21 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
示例 3
* 输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"]
* 输出: 22
* 解释:该算式转化为常见的中缀算术表达式为:
```
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
 
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
```
逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。
@ -62,7 +67,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
# 思路
在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)提到了 递归就是用栈来实现的。
在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)提到了 递归就是用栈来实现的。
所以**栈与递归之间在某种程度上是可以转换的!** 这一点我们在后续讲解二叉树的时候,会更详细的讲解到。
@ -70,12 +75,12 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。
在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)中的对对碰游戏是不是就非常像了。**
在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)中的对对碰游戏是不是就非常像了。**
如动画所示:
![150.逆波兰表达式求值](https://code-thinking.cdn.bcebos.com/gifs/150.逆波兰表达式求值.gif)
相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)是差不错的,只不过本题不要相邻元素做消除了,而是做运算!
C++代码如下:

View File

@ -467,6 +467,85 @@ function reverse(strArr, start, end) {
}
```
Swift:
```swift
func reverseWords(_ s: String) -> String {
var stringArr = removeSpace(s)
reverseString(&stringArr, startIndex: 0, endIndex: stringArr.count - 1)
reverseWord(&stringArr)
return String(stringArr)
}
/// 1、移除多余的空格前后所有的空格中间只留一个空格
func removeSpace(_ s: String) -> [Character] {
let ch = Array(s)
var left = 0
var right = ch.count - 1
// 忽略字符串前面的所有空格
while ch[left] == " " {
left += 1
}
// 忽略字符串后面的所有空格
while ch[right] == " " {
right -= 1
}
// 接下来就是要处理中间的多余空格
var lastArr = Array<Character>()
while left <= right {
// 准备加到新字符串当中的字符
let char = ch[left]
// 新的字符串的最后一个字符;或者原字符串中,准备加到新字符串的那个字符;这两个字符当中,只要有一个不是空格,就可以加到新的字符串当中
if char != " " || lastArr[lastArr.count - 1] != " " {
lastArr.append(char)
}
left += 1
}
return lastArr
}
/// 2、反转整个字符串
func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) {
var start = startIndex
var end = endIndex
while start < end {
(s[start], s[end]) = (s[end], s[start])
start += 1
end -= 1
}
}
/// 3、再次将字符串里面的单词反转
func reverseWord(_ s: inout [Character]) {
var start = 0
var end = 0
var entry = false
for i in 0..<s.count {
if !entry {
start = i
entry = true
}
if entry && s[i] == " " && s[i - 1] != " " {
end = i - 1
entry = false
reverseString(&s, startIndex: start, endIndex: end)
}
if entry && (i == s.count - 1) && s[i] != " " {
end = i
entry = false
reverseString(&s, startIndex: start, endIndex: end)
}
}
}
```

View File

@ -191,7 +191,37 @@ var isHappy = function(n) {
};
```
Swift
```swift
// number 每个位置上的数字的平方和
func getSum(_ number: Int) -> Int {
var sum = 0
var num = number
while num > 0 {
let temp = num % 10
sum += (temp * temp)
num /= 10
}
return sum
}
func isHappy(_ n: Int) -> Bool {
var set = Set<Int>()
var num = n
while true {
let sum = self.getSum(num)
if sum == 1 {
return true
}
// 如果这个sum曾经出现过说明已经陷入了无限循环了
if set.contains(sum) {
return false
} else {
set.insert(sum)
}
num = sum
}
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -97,6 +97,23 @@ class Solution {
## Go
```go
func isIsomorphic(s string, t string) bool {
map1 := make(map[byte]byte)
map2 := make(map[byte]byte)
for i := range s {
if _, ok := map1[s[i]]; !ok {
map1[s[i]] = t[i] // map1保存 s[i] 到 t[j]的映射
}
if _, ok := map2[t[i]]; !ok {
map2[t[i]] = s[i] // map2保存 t[i] 到 s[j]的映射
}
// 无法映射,返回 false
if (map1[s[i]] != t[i]) || (map2[t[i]] != s[i]) {
return false
}
}
return true
}
```
## JavaScript

View File

@ -264,6 +264,34 @@ impl Solution {
}
```
PHP:
```php
// 双指针 - 滑动窗口
class Solution {
/**
* @param Integer $target
* @param Integer[] $nums
* @return Integer
*/
function minSubArrayLen($target, $nums) {
if (count($nums) < 1) {
return 0;
}
$sum = 0;
$res = PHP_INT_MAX;
$left = 0;
for ($right = 0; $right < count($nums); $right++) {
$sum += $nums[$right];
while ($sum >= $target) {
$res = min($res, $right - $left + 1);
$sum -= $nums[$left];
$left++;
}
}
return $res == PHP_INT_MAX ? 0 : $res;
}
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -12,7 +12,7 @@
# 225. 用队列实现栈
https://leetcode-cn.com/problems/implement-stack-using-queues/
[力扣题目链接](https://leetcode-cn.com/problems/implement-stack-using-queues/)
使用队列实现栈的下列操作:
@ -34,7 +34,7 @@ https://leetcode-cn.com/problems/implement-stack-using-queues/
有的同学可能疑惑这种题目有什么实际工程意义,**其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样!**
刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/Cj6R0qu8rFA7Et9V_ZMjCA)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行!
刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://programmercarl.com/0232.用栈实现队列.html)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行!
**队列模拟栈,其实一个队列就够了**,那么我们先说一说两个队列来实现栈的思路。
@ -359,6 +359,71 @@ class MyStack:
Go
```go
type MyStack struct {
queue []int//创建一个队列
}
/** Initialize your data structure here. */
func Constructor() MyStack {
return MyStack{ //初始化
queue:make([]int,0),
}
}
/** Push element x onto stack. */
func (this *MyStack) Push(x int) {
//添加元素
this.queue=append(this.queue,x)
}
/** Removes the element on top of the stack and returns that element. */
func (this *MyStack) Pop() int {
n:=len(this.queue)-1//判断长度
for n!=0{ //除了最后一个,其余的都重新添加到队列里
val:=this.queue[0]
this.queue=this.queue[1:]
this.queue=append(this.queue,val)
n--
}
//弹出元素
val:=this.queue[0]
this.queue=this.queue[1:]
return val
}
/** Get the top element. */
func (this *MyStack) Top() int {
//利用Pop函数弹出来的元素重新添加
val:=this.Pop()
this.queue=append(this.queue,val)
return val
}
/** Returns whether the stack is empty. */
func (this *MyStack) Empty() bool {
return len(this.queue)==0
}
/**
* Your MyStack object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(x);
* param_2 := obj.Pop();
* param_3 := obj.Top();
* param_4 := obj.Empty();
*/
```
javaScript:
使用数组push, shift模拟队列

View File

@ -11,7 +11,7 @@
# 232.用栈实现队列
https://leetcode-cn.com/problems/implement-queue-using-stacks/
[力扣题目链接](https://leetcode-cn.com/problems/implement-queue-using-stacks/)
使用栈实现队列的下列操作:
@ -205,33 +205,26 @@ class MyQueue:
def pop(self) -> int:
"""
1. 检查如果out里面元素则直接pop
2. 如果out没有元素就把in里面的元素除了第一个依次pop后装进out里面
3. 直接把in剩下的元素pop出来就是queue头部的
Removes the element from in front of queue and returns that element.
"""
if self.empty:
if self.empty():
return None
if self.stack_out:
return self.stack_out.pop()
else:
for i in range(1, len(self.stack_in)):
for i in range(len(self.stack_in)):
self.stack_out.append(self.stack_in.pop())
return self.stack_in.pop()
return self.stack_out.pop()
def peek(self) -> int:
"""
1. 查out有没有元素有就把最上面的返回
2. 如果out没有元素就把in最下面的返回
Get the front element.
"""
if self.empty:
return None
if self.stack_out:
return self.stack_out[-1]
else:
return self.stack_in[0]
ans = self.pop()
self.stack_out.append(ans)
return ans
def empty(self) -> bool:

View File

@ -144,6 +144,75 @@ public:
## Java
```java
// 方法一,使用数组
class Solution {
public boolean isPalindrome(ListNode head) {
int len = 0;
// 统计链表长度
ListNode cur = head;
while (cur != null) {
len++;
cur = cur.next;
}
cur = head;
int[] res = new int[len];
// 将元素加到数组之中
for (int i = 0; i < res.length; i++){
res[i] = cur.val;
cur = cur.next;
}
// 比较回文
for (int i = 0, j = len - 1; i < j; i++, j--){
if (res[i] != res[j]){
return false;
}
}
return true;
}
}
// 方法二,快慢指针
class Solution {
public boolean isPalindrome(ListNode head) {
// 如果为空或者仅有一个节点返回true
if (head == null && head.next == null) return true;
ListNode slow = head;
ListNode fast = head;
ListNode pre = head;
while (fast != null && fast.next != null){
pre = slow; // 记录slow的前一个结点
slow = slow.next;
fast = fast.next.next;
}
pre.next = null; // 分割两个链表
// 前半部分
ListNode cur1 = head;
// 后半部分。这里使用了反转链表
ListNode cur2 = reverseList(slow);
while (cur1 != null){
if (cur1.val != cur2.val) return false;
// 注意要移动两个结点
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
}
ListNode reverseList(ListNode head){
// 反转链表
ListNode tmp = null;
ListNode pre = null;
while (head != null){
tmp = head.next;
head.next = pre;
pre = head;
head = tmp;
}
return pre;
}
}
```
## Python
@ -209,11 +278,13 @@ class Solution:
## Go
```go
```
## JavaScript
```js
```

View File

@ -7,7 +7,7 @@
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 235. 二叉搜索树的最近公共祖先
# 235. 二叉搜索树的最近公共祖先
[力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)
@ -21,14 +21,15 @@
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
* 输出: 6
* 解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
* 输出: 2
* 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
@ -36,7 +37,9 @@
* 所有节点的值都是唯一的。
* p、q 为不同节点且均存在于给定的二叉搜索树中。
## 思路
# 思路
做过[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)题目的同学应该知道利用回溯从底向上搜索遇到一个节点的左子树里有p右子树里有q那么当前节点就是最近公共祖先。
@ -58,6 +61,7 @@
可以看出直接按照指定的方向就可以找到节点4为最近公共祖先而且不需要遍历整棵树找到结果直接返回
## 递归法
递归三部曲如下:
@ -111,7 +115,6 @@ if (cur->val > p->val && cur->val > q->val) {
```
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
```
@ -128,7 +131,7 @@ left与right的逻辑处理;
如果 cur->val 小于 p->val同时 cur->val 小于 q->val那么就应该向右遍历目标区间在右子树
```
```CPP
if (cur->val < p->val && cur->val < q->val) {
TreeNode* right = traversal(cur->right, p, q);
if (right != NULL) {
@ -140,9 +143,9 @@ if (cur->val < p->val && cur->val < q->val) {
剩下的情况就是cur节点在区间p->val <= cur->val && cur->val <= q->val或者 q->val <= cur->val && cur->val <= p->val那么cur就是最近公共祖先了直接返回cur。
代码如下:
```
return cur;
```
那么整体递归代码如下:
@ -216,7 +219,7 @@ public:
灵魂拷问:是不是又被简单的迭代法感动到痛哭流涕?
## 总结
# 总结
对于二叉搜索树的最近祖先问题,其实要比[普通二叉树公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)简单的多。
@ -225,10 +228,15 @@ public:
最后给出了对应的迭代法,二叉搜索树的迭代法甚至比递归更容易理解,也是因为其有序性(自带方向性),按照目标区间找就行了。
## 其他语言版本
# 其他语言版本
Java
## Java
递归法:
迭代法:
```java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
@ -246,15 +254,11 @@ class Solution {
}
```
Python
```python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
## Python
递归法:
```python
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root: return root //
@ -264,18 +268,14 @@ class Solution:
return self.lowestCommonAncestor(root.right,p,q) //
else: return root
```
Go
> BSL法
迭代法:
## Go
递归法:
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//利用BSL的性质前序遍历有序
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root==nil{return nil}
@ -287,34 +287,10 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
}
```
> 普通法
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//递归会将值层层返回
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
//终止条件
if root==nil||root.Val==p.Val||root.Val==q.Val{return root}//最后为空或者找到一个值时,就返回这个值
//后序遍历
findLeft:=lowestCommonAncestor(root.Left,p,q)
findRight:=lowestCommonAncestor(root.Right,p,q)
//处理单层逻辑
if findLeft!=nil&&findRight!=nil{return root}//说明在root节点的两边
if findLeft==nil{//左边没找到,就说明在右边找到了
return findRight
}else {return findLeft}
}
```
## JavaScript
JavaScript版本
1. 使用递归的方法
递归法
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用递归的方法
@ -336,7 +312,8 @@ var lowestCommonAncestor = function(root, p, q) {
return root;
};
```
2. 使用迭代的方法
迭代法
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用迭代的方法
@ -355,7 +332,6 @@ var lowestCommonAncestor = function(root, p, q) {
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -9,7 +9,7 @@
> 本来是打算将二叉树和二叉搜索树的公共祖先问题一起讲,后来发现篇幅过长了,只能先说一说二叉树的公共祖先问题。
## 236. 二叉树的最近公共祖先
# 236. 二叉树的最近公共祖先
[力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/)
@ -35,7 +35,7 @@
* 所有节点的值都是唯一的。
* p、q 为不同节点且均存在于给定的二叉树中。
## 思路
# 思路
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
@ -202,7 +202,7 @@ public:
};
```
## 总结
# 总结
这道题目刷过的同学未必真正了解这里面回溯的过程,以及结果是如何一层一层传上去的。
@ -219,10 +219,10 @@ public:
本题没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了。
## 其他语言版本
# 其他语言版本
Java
## Java
```Java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
@ -261,14 +261,9 @@ class Solution {
}
```
Python
## Python
```python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
//递归
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
@ -280,7 +275,9 @@ class Solution:
elif not left and right: return right //目标节点是通过right返回的
else: return None //没找到
```
Go
## Go
```Go
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
// check
@ -310,7 +307,8 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
}
```
JavaScript版本:
## JavaScript
```javascript
var lowestCommonAncestor = function(root, p, q) {
// 使用递归的方法

View File

@ -9,7 +9,7 @@
## 279.完全平方数
题目地址:https://leetcode-cn.com/problems/perfect-squares/
[力扣题目链接](https://leetcode-cn.com/problems/perfect-squares/)
给定正整数 n找到若干个完全平方数比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
@ -36,7 +36,7 @@
**我来把题目翻译一下完全平方数就是物品可以无限件使用凑个正整数n就是背包问凑满这个背包最少有多少物品**
感受出来了没,这么浓厚的完全背包氛围,而且和昨天的题目[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)就是一样一样的!
感受出来了没,这么浓厚的完全背包氛围,而且和昨天的题目[动态规划322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)就是一样一样的!
动规五部曲分析如下:
@ -70,7 +70,7 @@ dp[0]表示 和为0的完全平方数的最小数量那么dp[0]一定是0。
如果求排列数就是外层for遍历背包内层for循环遍历物品。
在[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)中我们就深入探讨了这个问题,本题也是一样的,是求最小数!
在[动态规划322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)中我们就深入探讨了这个问题,本题也是一样的,是求最小数!
**所以本题外层for遍历背包里层for遍历物品还是外层for遍历物品内层for遍历背包都是可以的**
@ -146,7 +146,7 @@ public:
## 总结
如果大家认真做了昨天的题目[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ),今天这道就非常简单了,一样的套路一样的味道。
如果大家认真做了昨天的题目[动态规划322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html),今天这道就非常简单了,一样的套路一样的味道。
但如果没有按照「代码随想录」的题目顺序来做的话,做动态规划或者做背包问题,上来就做这道题,那还是挺难的!

View File

@ -8,13 +8,13 @@
## 300.最长递增子序列
题目链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/
[力扣题目链接](https://leetcode-cn.com/problems/longest-increasing-subsequence/)
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
 
示例 1
输入nums = [10,9,2,5,3,7,101,18]
输出4
@ -27,7 +27,7 @@
示例 3
输入nums = [7,7,7,7,7,7,7]
输出1
 
提示:
* 1 <= nums.length <= 2500

View File

@ -29,7 +29,7 @@
与198.打家劫舍213.打家劫舍II一样关键是要讨论当前节点抢还是不抢。
如果抢了当前节点,两个孩子就不动,如果没抢当前节点,就可以考虑抢左右孩子(**注意这里说的是“考虑”**
如果抢了当前节点,两个孩子就不动,如果没抢当前节点,就可以考虑抢左右孩子(**注意这里说的是“考虑”**
### 暴力递归
@ -91,7 +91,7 @@ public:
### 动态规划
在上面两种方法,其实对一个节点 与不得到的最大金钱都没有做记录,而是需要实时计算。
在上面两种方法,其实对一个节点 与不得到的最大金钱都没有做记录,而是需要实时计算。
而动态规划其实就是使用状态转移容器来记录状态的变化这里可以使用一个长度为2的数组记录当前节点偷与不偷所得到的的最大金钱。
@ -121,7 +121,7 @@ vector<int> robTree(TreeNode* cur) {
2. 确定终止条件
在遍历的过程中,如果遇到空点的话很明显无论偷还是不偷都是0所以就返回
在遍历的过程中,如果遇到空点的话很明显无论偷还是不偷都是0所以就返回
```
if (cur == NULL) return vector<int>{0, 0};
```

View File

@ -12,7 +12,7 @@
# 344.反转字符串
https://leetcode-cn.com/problems/reverse-string/
[力扣题目链接](https://leetcode-cn.com/problems/reverse-string/)
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
@ -55,7 +55,7 @@ https://leetcode-cn.com/problems/reverse-string/
接下来再来讲一下如何解决反转字符串的问题。
大家应该还记得,我们已经讲过了[206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)。
大家应该还记得,我们已经讲过了[206.反转链表](https://programmercarl.com/0206.翻转链表.html)。
在反转链表中,使用了双指针的方法。
@ -63,7 +63,7 @@ https://leetcode-cn.com/problems/reverse-string/
因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。
如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/fDGMmLrW7ZHlzkzlf_dZkw)[必须掌握的数组理论知识](https://mp.weixin.qq.com/s/c2KABb-Qgg66HrGf8z-8Og)。
如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://programmercarl.com/链表理论基础.html)[必须掌握的数组理论知识](https://programmercarl.com/数组理论基础.html)。
对于字符串,我们定义两个指针(也可以说是索引下表),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。

View File

@ -14,7 +14,7 @@
## 349. 两个数组的交集
https://leetcode-cn.com/problems/intersection-of-two-arrays/
[力扣题目链接](https://leetcode-cn.com/problems/intersection-of-two-arrays/)
题意:给定两个数组,编写一个函数来计算它们的交集。
@ -32,7 +32,7 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/
这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。
那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)
那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)
但是要注意,**使用数组来做哈希的题目,是因为题目都限制了数值的大小。**
@ -192,6 +192,22 @@ var intersection = function(nums1, nums2) {
};
```
Swift
```swift
func intersection(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
var set1 = Set<Int>()
var set2 = Set<Int>()
for num in nums1 {
set1.insert(num)
}
for num in nums2 {
if set1.contains(num) {
set2.insert(num)
}
}
return Array(set2)
}
```
## 相关题目

View File

@ -203,6 +203,25 @@ const isSubsequence = (s, t) => {
};
```
Go
```go
func isSubsequence(s string, t string) bool {
dp := make([][]int,len(s)+1)
for i:=0;i<len(dp);i++{
dp[i] = make([]int,len(t)+1)
}
for i:=1;i<len(dp);i++{
for j:=1;j<len(dp[i]);j++{
if s[i-1] == t[j-1]{
dp[i][j] = dp[i-1][j-1] +1
}else{
dp[i][j] = dp[i][j-1]
}
}
}
return dp[len(s)][len(t)]==len(s)
}
```

View File

@ -197,11 +197,10 @@ func findContentChildren(g []int, s []int) int {
return child
}
```
Javascript:
```Javascript
```
var findContentChildren = function(g, s) {
g = g.sort((a, b) => a - b)
s = s.sort((a, b) => a - b)

View File

@ -128,7 +128,10 @@ x = (S + sum) / 2
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
```
**看到这种表达式应该本能的反应两个int相加数值可能溢出的问题当然本题并没有溢出**
同时如果 S的绝对值已经大于sum那么也是没有方案的
```CPP
if (abs(S) > sum) return 0; // 此时没有方案
```
再回归到01背包问题为什么是01背包呢
@ -200,7 +203,7 @@ public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for (int i = 0; i < nums.size(); i++) sum += nums[i];
if (S > sum) return 0; // 此时没有方案
if (abs(S) > sum) return 0; // 此时没有方案
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
int bagSize = (S + sum) / 2;
vector<int> dp(bagSize + 1, 0);

View File

@ -9,7 +9,7 @@
> 二叉树上应该怎么求,二叉搜索树上又应该怎么求?
## 501.二叉搜索树中的众数
# 501.二叉搜索树中的众数
[力扣题目链接](https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/solution/)
@ -33,7 +33,7 @@
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
## 思路
# 思路
这道题目呢,递归法我从两个维度来讲。
@ -321,7 +321,7 @@ public:
};
```
## 总结
# 总结
本题在递归法中,我给出了如果是普通二叉树,应该怎么求众数。
@ -340,12 +340,13 @@ public:
> **需要强调的是 leetcode上的耗时统计是非常不准确的看个大概就行一样的代码耗时可以差百分之50以上**所以leetcode的耗时统计别太当回事知道理论上的效率优劣就行了。
## 其他语言版本
# 其他语言版本
Java
## Java
暴力法
```java
class Solution {
public int[] findMode(FindModeInBinarySearchTree.TreeNode root) {
@ -379,6 +380,8 @@ class Solution {
}
```
中序遍历-不使用额外空间,利用二叉搜索树特性
```Java
class Solution {
ArrayList<Integer> resList;
@ -427,15 +430,11 @@ class Solution {
}
```
Python
## Python
递归法
```python
# 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 findMode(self, root: TreeNode) -> List[int]:
if not root: return
@ -460,36 +459,11 @@ class Solution:
return
findNumber(root)
return self.res
```
# 迭代法-中序遍历-使用额外空间map的方法
class Solution:
def findMode(self, root: TreeNode) -> List[int]:
stack = []
cur = root
pre = None
dist = {}
while cur or stack:
if cur: # 指针来访问节点,访问到最底层
stack.append(cur)
cur = cur.left
else: # 逐一处理节点
cur = stack.pop()
if cur.val in dist:
dist[cur.val] += 1
else:
dist[cur.val] = 1
pre = cur
cur = cur.right
# 找出字典中最大的key
res = []
for key, value in dist.items():
if (value == max(dist.values())):
res.append(key)
return res
# 迭代法-中序遍历-不使用额外空间,利用二叉搜索树特性:
迭代法-中序遍历-使用额外空间,利用二叉搜索树特性
```python
class Solution:
def findMode(self, root: TreeNode) -> List[int]:
stack = []
@ -521,18 +495,11 @@ class Solution:
return res
```
Go
## Go
暴力法非BSL
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func findMode(root *TreeNode) []int {
var history map[int]int
var maxValue int
@ -571,15 +538,7 @@ func traversal(root *TreeNode,history map[int]int){
计数法,不使用额外空间,利用二叉树性质,中序遍历
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func findMode(root *TreeNode) []int {
func findMode(root *TreeNode) []int {
res := make([]int, 0)
count := 1
max := 1
@ -611,8 +570,9 @@ func traversal(root *TreeNode,history map[int]int){
}
```
JavaScript版本:
使用额外空间map的方法
## JavaScript
使用额外空间map的方法
```javascript
var findMode = function(root) {
// 使用递归中序遍历
@ -649,8 +609,10 @@ var findMode = function(root) {
}
return res;
};
```
```
不使用额外空间,利用二叉树性质,中序遍历(有序)
```javascript
var findMode = function(root) {
// 不使用额外空间,使用中序遍历,设置出现最大次数初始值为1

View File

@ -265,7 +265,7 @@ func getMinimumDifference(root *TreeNode) int {
```
## JavaScript
递归 先转换为有序数组
```javascript
/**
* Definition for a binary tree node.
@ -297,6 +297,47 @@ var getMinimumDifference = function (root) {
return diff;
};
```
递归 在递归的过程中更新最小值
```js
var getMinimumDifference = function(root) {
let res = Infinity
let preNode = null
// 中序遍历
const inorder = (node) => {
if(!node) return
inorder(node.left)
// 更新res
if(preNode) res = Math.min(res, node.val - preNode.val)
// 记录前一个节点
preNode = node
inorder(node.right)
}
inorder(root)
return res
}
```
迭代 中序遍历
```js
var getMinimumDifference = function(root) {
let stack = []
let cur = root
let res = Infinity
let pre = null
while(cur || stack.length) {
if(cur) {
stack.push(cur)
cur = cur.left
} else {
cur = stack.pop()
if(pre) res = Math.min(res, cur.val - pre.val)
pre = cur
cur = cur.right
}
}
return res
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -12,7 +12,7 @@
# 541. 反转字符串II
https://leetcode-cn.com/problems/reverse-string-ii/
[力扣题目链接](https://leetcode-cn.com/problems/reverse-string-ii/)
给定一个字符串 s 和一个整数 k你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
@ -65,7 +65,7 @@ public:
};
```
那么我们也可以实现自己的reverse函数其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)道理是一样的。
那么我们也可以实现自己的reverse函数其实和题目[344. 反转字符串](https://programmercarl.com/0344.反转字符串.html)道理是一样的。
下面我实现的reverse函数区间是左闭右闭区间代码如下
@ -254,6 +254,28 @@ var reverseStr = function(s, k) {
```
Swift:
```swift
func reverseStr(_ s: String, _ k: Int) -> String {
var ch = Array(s)
for i in stride(from: 0, to: ch.count, by: 2 * k) {
var left = i
var right = min(s.count - 1, left + k - 1)
while left < right {
(ch[left], ch[right]) = (ch[right], ch[left])
left += 1
right -= 1
}
}
return String(ch)
}
```
-----------------------

View File

@ -147,8 +147,38 @@ class Solution:
```
Go
```go
func minDistance(word1 string, word2 string) int {
dp := make([][]int, len(word1)+1)
for i := 0; i < len(dp); i++ {
dp[i] = make([]int, len(word2)+1)
}
//初始化
for i := 0; i < len(dp); i++ {
dp[i][0] = i
}
for j := 0; j < len(dp[0]); j++ {
dp[0][j] = j
}
for i := 1; i < len(dp); i++ {
for j := 1; j < len(dp[i]); j++ {
if word1[i-1] == word2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else {
dp[i][j] = min(min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+2)
}
}
}
return dp[len(dp)-1][len(dp[0])-1]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
```
Javascript
```javascript
const minDistance = (word1, word2) => {

View File

@ -9,6 +9,10 @@
# 673.最长递增子序列的个数
[力扣题目链接](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/)
给定一个未排序的整数数组,找到最长递增子序列的个数。
示例 1:
@ -224,16 +228,110 @@ public:
## Java
```java
class Solution {
public int findNumberOfLIS(int[] nums) {
if (nums.length <= 1) return nums.length;
int[] dp = new int[nums.length];
for(int i = 0; i < dp.length; i++) dp[i] = 1;
int[] count = new int[nums.length];
for(int i = 0; i < count.length; i++) count[i] = 1;
int maxCount = 0;
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
count[i] = count[j];
} else if (dp[j] + 1 == dp[i]) {
count[i] += count[j];
}
}
if (dp[i] > maxCount) maxCount = dp[i];
}
}
int result = 0;
for (int i = 0; i < nums.length; i++) {
if (maxCount == dp[i]) result += count[i];
}
return result;
}
}
```
## Python
```python
class Solution:
def findNumberOfLIS(self, nums: List[int]) -> int:
size = len(nums)
if size<= 1: return size
dp = [1 for i in range(size)]
count = [1 for i in range(size)]
maxCount = 0
for i in range(1, size):
for j in range(i):
if nums[i] > nums[j]:
if dp[j] + 1 > dp[i] :
dp[i] = dp[j] + 1
count[i] = count[j]
elif dp[j] + 1 == dp[i] :
count[i] += count[j]
if dp[i] > maxCount:
maxCount = dp[i];
result = 0
for i in range(size):
if maxCount == dp[i]:
result += count[i]
return result;
```
## Go
```go
func findNumberOfLIS(nums []int) int {
size := len(nums)
if size <= 1 {
return size
}
dp := make([]int, size);
for i, _ := range dp {
dp[i] = 1
}
count := make([]int, size);
for i, _ := range count {
count[i] = 1
}
maxCount := 0
for i := 1; i < size; i++ {
for j := 0; j < i; j++ {
if nums[i] > nums[j] {
if dp[j] + 1 > dp[i] {
dp[i] = dp[j] + 1
count[i] = count[j]
} else if dp[j] + 1 == dp[i] {
count[i] += count[j]
}
}
if dp[i] > maxCount {
maxCount = dp[i]
}
}
}
result := 0
for i := 0; i < size; i++ {
if maxCount == dp[i] {
result += count[i]
}
}
return result
}
```
## JavaScript

View File

@ -478,6 +478,38 @@ int search(int* nums, int numsSize, int target){
}
```
**PHP:**
```php
// 左闭右闭区间
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer
*/
function search($nums, $target) {
if (count($nums) == 0) {
return -1;
}
$left = 0;
$right = count($nums) - 1;
while ($left <= $right) {
$mid = floor(($left + $right) / 2);
if ($nums[$mid] == $target) {
return $mid;
}
if ($nums[$mid] > $target) {
$right = $mid - 1;
}
else {
$left = $mid + 1;
}
}
return -1;
}
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -152,6 +152,25 @@ class Solution:
```
Go
```Go
func maxProfit(prices []int, fee int) int {
n := len(prices)
dp := make([][2]int, n)
dp[0][0] = -prices[0]
for i := 1; i < n; i++ {
dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]-fee)
dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i])
}
return dp[n-1][1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
Javascript
```javascript

View File

@ -368,7 +368,34 @@ class Solution:
return result
```
Go
```go
const inf = math.MaxInt64 / 2
func minCameraCover(root *TreeNode) int {
var dfs func(*TreeNode) (a, b, c int)
dfs = func(node *TreeNode) (a, b, c int) {
if node == nil {
return inf, 0, 0
}
lefta, leftb, leftc := dfs(node.Left)
righta, rightb, rightc := dfs(node.Right)
a = leftc + rightc + 1
b = min(a, min(lefta+rightb, righta+leftb))
c = min(a, leftb+rightb)
return
}
_, ans, _ := dfs(root)
return ans
}
func min(a, b int) int {
if a <= b {
return a
}
return b
}
```
Javascript:
```Javascript
var minCameraCover = function(root) {

View File

@ -270,6 +270,7 @@ def sorted_squares(nums)
end
```
C:
```c
int* sortedSquares(int* nums, int numsSize, int* returnSize){
@ -303,6 +304,37 @@ int* sortedSquares(int* nums, int numsSize, int* returnSize){
}
```
PHP:
```php
class Solution {
/**
* @param Integer[] $nums
* @return Integer[]
*/
function sortedSquares($nums) {
// 双指针法
$res = [];
for ($i = 0; $i < count($nums); $i++) {
$res[$i] = 0;
}
$k = count($nums) - 1;
for ($i = 0, $j = count($nums) - 1; $i <= $j; ) {
if ($nums[$i] ** 2 < $nums[$j] ** 2) {
$res[$k--] = $nums[$j] ** 2;
$j--;
}
else {
$res[$k--] = $nums[$i] ** 2;
$i++;
}
}
return $res;
}
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -10,7 +10,7 @@
# 1002. 查找常用字符
https://leetcode-cn.com/problems/find-common-characters/
[力扣题目链接](https://leetcode-cn.com/problems/find-common-characters/)
给定仅有小写字母组成的字符串数组 A返回列表中的每个字符串中都显示的全部字符包括重复字符组成的列表。例如如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/find-common-characters/
【示例二】
输入:["cool","lock","cook"]
输出:["c","o"]
 
# 思路
@ -40,9 +40,9 @@ https://leetcode-cn.com/problems/find-common-characters/
可以看出这是指数级别的时间复杂度,非常高,而且代码实现也不容易,因为要统计 重复的字符,还要适当的替换或者去重。
那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。
那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)。
如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)。
如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://programmercarl.com/0242.有效的字母异位词.html)。
了解了哈希法,理解了数组在哈希法中的应用之后,可以来看解题思路了。
@ -268,6 +268,47 @@ func min(a,b int)int{
return a
}
```
Swift
```swift
func commonChars(_ words: [String]) -> [String] {
var res = [String]()
if words.count < 1 {
return res
}
let aUnicodeScalarValue = "a".unicodeScalars.first!.value
let lettersMaxCount = 26
// 用于统计所有字符串每个字母出现的 最小 频率
var hash = Array(repeating: 0, count: lettersMaxCount)
// 统计第一个字符串每个字母出现的次数
for unicodeScalar in words.first!.unicodeScalars {
hash[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
}
// 统计除第一个字符串每个字母出现的次数
for idx in 1 ..< words.count {
var hashOtherStr = Array(repeating: 0, count: lettersMaxCount)
for unicodeScalar in words[idx].unicodeScalars {
hashOtherStr[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1
}
// 更新hash,保证hash里统计的字母为出现的最小频率
for k in 0 ..< lettersMaxCount {
hash[k] = min(hash[k], hashOtherStr[k])
}
}
// 将hash统计的字符次数转成输出形式
for i in 0 ..< lettersMaxCount {
while hash[i] != 0 { // 注意这里是while多个重复的字符
let currentUnicodeScalarValue: UInt32 = UInt32(i) + aUnicodeScalarValue
let currentUnicodeScalar: UnicodeScalar = UnicodeScalar(currentUnicodeScalarValue)!
let outputStr = String(currentUnicodeScalar) // UnicodeScalar -> String
res.append(outputStr)
hash[i] -= 1
}
}
return res
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -110,18 +110,16 @@ class Solution {
int len = nums.length;
for (int i = 0; i < len; i++) {
//从前向后遍历遇到负数将其变为正数同时K--
if (nums[i] < 0 && k > 0) {
if (nums[i] < 0 && K > 0) {
nums[i] = -nums[i];
k--;
K--;
}
}
// 如果K还大于0那么反复转变数值最小的元素将K用完
if (k % 2 == 1) nums[len - 1] = -nums[len - 1];
int result = 0;
for (int a : nums) {
result += a;
}
return result;
if (K % 2 == 1) nums[len - 1] = -nums[len - 1];
return Arrays.stream(nums).sum();
}
}
```

View File

@ -13,7 +13,7 @@
# 1047. 删除字符串中的所有相邻重复项
https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
[力扣题目链接](https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/)
给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母并删除它们。
@ -26,7 +26,7 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
* 输入:"abbaca"
* 输出:"ca"
* 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
 
提示:
* 1 <= S.length <= 20000

View File

@ -215,7 +215,7 @@ class TreeNode:
```
Go
```
```go
type TreeNode struct {
Val int
Left *TreeNode
@ -224,7 +224,7 @@ type TreeNode struct {
```
JavaScript
```
```javascript
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)

View File

@ -5,3 +5,7 @@
**push代码之前 一定要 先pull最新代码**否则提交的pr可能会有删除其他录友代码的操作。
一个pr 不要修改过多文件,因为一旦有一个 文件修改有问题,就不能合入,影响其他文件的合入了。
git add之前要git diff 查看一下,本次提交所修改的代码是不是 自己修改的,是否 误删,或者误加的文件。
提交代码不要使用git push -f 这种命令,要足够了解 -f 意味着什么。

View File

@ -51,7 +51,7 @@
## 总结
同在广东省,难免不了要和深圳对比,大家如果看了这篇:[深圳原来有这么多互联网公司,你都知道么?](https://mp.weixin.qq.com/s/3VJHF2zNohBwDBxARFIn-Q)就能感受到鲜明的对比了。
同在广东省,难免不了要和深圳对比,大家如果看了这篇:[深圳原来有这么多互联网公司,你都知道么?](https://programmercarl.com/前序/深圳互联网公司总结.html)就能感受到鲜明的对比了。
广州大厂高端岗位其实比较少,本土只有微信和网易,微信呢毕竟还是腾讯的分部,而网易被很多人认为是杭州企业,其实网易总部在广州。

View File

@ -264,6 +264,49 @@ javaScript:
};
```
Swift:
```swift
func replaceSpace(_ s: String) -> String {
var strArr = Array(s)
var count = 0
// 统计空格的个数
for i in strArr {
if i == " " {
count += 1
}
}
// left 指向旧数组的最后一个元素
var left = strArr.count - 1
// right 指向扩容后数组的最后一个元素(这里还没对数组进行实际上的扩容)
var right = strArr.count + count * 2 - 1
// 实际对数组扩容
for _ in 0..<(count * 2) {
strArr.append(" ")
}
while left < right {
if strArr[left] == " " {
strArr[right] = "0"
strArr[right - 1] = "2"
strArr[right - 2] = "%"
left -= 1
right -= 3
} else {
strArr[right] = strArr[left]
left -= 1
right -= 1
}
}
return String(strArr)
}
```
-----------------------

View File

@ -214,6 +214,34 @@ var reverseLeftWords = function (s, n) {
};
```
Swift:
```swift
func reverseLeftWords(_ s: String, _ n: Int) -> String {
var ch = Array(s)
let len = ch.count
// 反转区间[0, n - 1]
reverseString(&ch, startIndex: 0, endIndex: n - 1)
// 反转区间[n, len - 1]
reverseString(&ch, startIndex: n, endIndex: len - 1)
// 反转区间[0, len - 1],也就是整个字符串反转
reverseString(&ch, startIndex: 0, endIndex: len - 1)
return String(ch)
}
func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) {
var start = startIndex
var end = endIndex
while start < end {
(s[start], s[end]) = (s[end], s[start])
start += 1
end -= 1
}
}
```

View File

@ -3,7 +3,7 @@
## 周一
在[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中我们讲解了动态规划的基础知识。
在[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)中我们讲解了动态规划的基础知识。
首先讲一下动规和贪心的区别,其实大家不用太强调理论上的区别,做做题,就感受出来了。
@ -33,13 +33,13 @@
## 周二
这道题目[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)是当之无愧的动规入门题。
这道题目[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)是当之无愧的动规入门题。
简单题我们就是用来了解方法论的用动规五部曲走一遍题目其实已经把递推公式和dp数组如何初始化都给我们了。
## 周三
[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw) 这道题目其实就是斐波那契数列。
[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html) 这道题目其实就是斐波那契数列。
但正常思考过程应该是推导完递推公式之后,发现这是斐波那契,而不是上来就知道这是斐波那契。
@ -98,11 +98,11 @@ public:
这道绝佳的面试题我没有用过,如果录友们有面试别人的需求,就把这个套路拿去吧,哈哈哈。
我在[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ)中以我自己面试别人的真实经历通过求x的n次方 这么简单的题目,就可以考察候选人对算法性能以及递归的理解深度,录友们可以看看,绝对有收获!
我在[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://programmercarl.com/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.html)中以我自己面试别人的真实经历通过求x的n次方 这么简单的题目,就可以考察候选人对算法性能以及递归的理解深度,录友们可以看看,绝对有收获!
## 周四
这道题目[动态规划:使用最小花费爬楼梯](https://mp.weixin.qq.com/s/djZB9gkyLFAKcQcSvKDorA)就是在爬台阶的基础上加了一个花费,
这道题目[动态规划:使用最小花费爬楼梯](https://programmercarl.com/0746.使用最小花费爬楼梯.html)就是在爬台阶的基础上加了一个花费,
这道题描述也确实有点魔幻。

View File

@ -1,7 +1,7 @@
## 周一
[动态规划:不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)中求从出发点到终点有几种路径,只能向下或者向右移动一步。
[动态规划:不同路径](https://programmercarl.com/0062.不同路径.html)中求从出发点到终点有几种路径,只能向下或者向右移动一步。
我们提供了三种方法,但重点讲解的还是动规,也是需要重点掌握的。
@ -35,7 +35,7 @@ for (int i = 1; i < m; i++) {
## 周二
[动态规划:不同路径还不够,要有障碍!](https://mp.weixin.qq.com/s/lhqF0O4le9-wvalptOVOww)相对于[动态规划:不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)添加了障碍。
[动态规划:不同路径还不够,要有障碍!](https://programmercarl.com/0063.不同路径II.html)相对于[动态规划:不同路径](https://programmercarl.com/0062.不同路径.html)添加了障碍。
dp[i][j]定义依然是表示从0 0出发到(i, j) 有dp[i][j]条不同的路径。
@ -78,7 +78,7 @@ for (int i = 1; i < m; i++) {
## 周三
[动态规划:整数拆分,你要怎么拆?](https://mp.weixin.qq.com/s/cVbyHrsWH_Rfzlj-ESr01A)给出一个整数,问有多少种拆分的方法。
[动态规划:整数拆分,你要怎么拆?](https://programmercarl.com/0343.整数拆分.html)给出一个整数,问有多少种拆分的方法。
这道题目就有点难度了题目中dp我也给出了两种方法但通过两种方法的比较可以看出对dp数组定义的理解以及dp数组初始化的重要性。
@ -121,7 +121,7 @@ for (int i = 3; i <= n ; i++) {
**或者也可以理解j是拆分i的第一个整数**
[动态规划:整数拆分,你要怎么拆?](https://mp.weixin.qq.com/s/cVbyHrsWH_Rfzlj-ESr01A)总结里我也给出了递推公式dp[i] = max(dp[i], dp[i - j] * dp[j])这种写法。
[动态规划:整数拆分,你要怎么拆?](https://programmercarl.com/0343.整数拆分.html)总结里我也给出了递推公式dp[i] = max(dp[i], dp[i - j] * dp[j])这种写法。
对于这种写法一位录友总结的很好意思就是如果递推公式是dp[i-j] * dp[j],这样就相当于强制把一个数至少拆分成四份。
@ -129,7 +129,7 @@ dp[i-j]至少是两个数的乘积dp[j]又至少是两个数的乘积,但
## 周四
[动态规划:不同的二叉搜索树](https://mp.weixin.qq.com/s/8VE8pDrGxTf8NEVYBDwONw)给出n个不同的节点求能组成多少个不同二叉搜索树。
[动态规划:不同的二叉搜索树](https://programmercarl.com/0096.不同的二叉搜索树.html)给出n个不同的节点求能组成多少个不同二叉搜索树。
这道题目还是比较难的,想到用动态规划的方法就很不容易了!
@ -145,7 +145,7 @@ n为5时候的dp数组状态如图
## 总结
本周题目已经开始点难度了,特别是[动态规划:不同的二叉搜索树](https://mp.weixin.qq.com/s/8VE8pDrGxTf8NEVYBDwONw)这道题目,明显感觉阅读量很低,可能是因为确实有点难吧。
本周题目已经开始点难度了,特别是[动态规划:不同的二叉搜索树](https://programmercarl.com/0096.不同的二叉搜索树.html)这道题目,明显感觉阅读量很低,可能是因为确实有点难吧。
我现在也陷入了纠结,题目一简单,就会有录友和我反馈说题目太简单了,题目一难,阅读量就特别低。

View File

@ -7,7 +7,7 @@
## 周一
[动态规划关于01背包问题你该了解这些](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中,我们开始介绍了背包问题。
[动态规划关于01背包问题你该了解这些](https://programmercarl.com/背包理论基础01背包-1.html)中,我们开始介绍了背包问题。
首先对于背包的所有问题中01背包是最最基础的其他背包也是在01背包的基础上稍作变化。
@ -75,7 +75,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
## 周二
[动态规划关于01背包问题你该了解这些滚动数组](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中把01背包的一维dp数组滚动数组实现详细讲解了一遍。
[动态规划关于01背包问题你该了解这些滚动数组](https://programmercarl.com/背包理论基础01背包-2.html)中把01背包的一维dp数组滚动数组实现详细讲解了一遍。
分析一下和二维dp数组有什么区别在初始化和遍历顺序上又有什么差异
@ -125,7 +125,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
## 周三
[动态规划416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)中我们开始用01背包来解决问题。
[动态规划416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)中我们开始用01背包来解决问题。
只有确定了如下四点才能把01背包问题套到本题上来。
@ -138,11 +138,11 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
## 周四
[动态规划1049. 最后一块石头的重量 II](https://mp.weixin.qq.com/s/WbwAo3jaUaNJjvhHgq0BGg)这道题目其实和[动态规划416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)是非常像的。
[动态规划1049. 最后一块石头的重量 II](https://programmercarl.com/1049.最后一块石头的重量II.html)这道题目其实和[动态规划416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)是非常像的。
本题其实就是尽量让石头分成重量相同的两堆相撞之后剩下的石头最小这样就化解成01背包问题了。
[动态规划416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)相当于是求背包是否正好装满,而本题是求背包最多能装多少。
[动态规划416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)相当于是求背包是否正好装满,而本题是求背包最多能装多少。
这两道题目是对dp[target]的处理方式不同。这也考验的对dp[i]定义的理解。

View File

@ -2,7 +2,7 @@
## 周一
[动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)要求在数列之间加入+ 或者 -使其和为S。
[动态规划:目标和!](https://programmercarl.com/0494.目标和.html)要求在数列之间加入+ 或者 -使其和为S。
所有数的总和为sum假设加法的总和为x那么可以推出x = (S + sum) / 2。
@ -39,7 +39,7 @@ dp数组状态变化如下
## 周二
这道题目[动态规划:一和零!](https://mp.weixin.qq.com/s/x-u3Dsp76DlYqtCe0xEKJw)算有点难度。
这道题目[动态规划:一和零!](https://programmercarl.com/0474.一和零.html)算有点难度。
**不少同学都以为是多重背包其实这是一道标准的01背包**
@ -78,7 +78,7 @@ dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
此时01背包我们就讲完了正式开始完全背包。
在[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)中我们讲解了完全背包的理论基础。
在[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)中我们讲解了完全背包的理论基础。
其实完全背包和01背包区别就是完全背包的物品是无限数量。
@ -100,7 +100,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
**那么为什么要先遍历物品,在遍历背包呢?** (灵魂拷问)
其实对于纯完全背包,先遍历物品,再遍历背包 与 先遍历背包,再遍历物品都是可以的。我在文中[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)也给出了详细的解释。
其实对于纯完全背包,先遍历物品,再遍历背包 与 先遍历背包,再遍历物品都是可以的。我在文中[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)也给出了详细的解释。
这个细节是很多同学忽略掉的点,其实也不算细节了,**相信不少同学在写背包的时候两层for循环的先后循序搞不清楚靠感觉来的**。
@ -110,7 +110,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
## 周四
在[动态规划:给你一些零钱,你要怎么凑?](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)中就是给你一堆零钱零钱个数无限为凑成amount的组合数有几种。
在[动态规划:给你一些零钱,你要怎么凑?](https://programmercarl.com/0518.零钱兑换II.html)中就是给你一堆零钱零钱个数无限为凑成amount的组合数有几种。
**注意这里组合数和排列数的区别!**
@ -134,7 +134,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
其实这是一种错觉,或者说对动规理解的不够深入!
我在动规专题开篇介绍[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中就强调了 **递推公式仅仅是 动规五部曲里的一小部分, dp数组的定义、初始化、遍历顺序哪一点没有搞透的话即使知道递推公式遇到稍稍难一点的动规题目立刻会感觉写不出来了**
我在动规专题开篇介绍[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)中就强调了 **递推公式仅仅是 动规五部曲里的一小部分, dp数组的定义、初始化、遍历顺序哪一点没有搞透的话即使知道递推公式遇到稍稍难一点的动规题目立刻会感觉写不出来了**
此时相信大家对动规五部曲也有更深的理解了同样也验证了Carl之前讲过的**简单题是用来学习方法论的,而遇到难题才体现出方法论的重要性!**

View File

@ -2,7 +2,7 @@
## 周一
[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)中给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数(顺序不同的序列被视作不同的组合)。
[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)中给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数(顺序不同的序列被视作不同的组合)。
题目面试虽然是组合,但又强调顺序不同的序列被视作不同的组合,其实这道题目求的是排列数!
@ -10,7 +10,7 @@
这个和前上周讲的组合问题又不一样,关键就体现在遍历顺序上!
在[动态规划518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ) 中就已经讲过了。
在[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 中就已经讲过了。
**如果求组合数就是外层for循环遍历物品内层for遍历背包**
@ -40,7 +40,7 @@ public:
## 周二
爬楼梯之前我们已经做过了,就是斐波那契数列,很好解,但[动态规划70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)中我们进阶了一下。
爬楼梯之前我们已经做过了,就是斐波那契数列,很好解,但[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)中我们进阶了一下。
改为:每次可以爬 1 、 2、.....、m 个台阶。问有多少种不同的方法可以爬到楼顶呢?
@ -53,7 +53,7 @@ public:
**此时大家应该发现这就是一个完全背包问题了!**
和昨天的题目[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)基本就是一道题了,遍历顺序也是一样一样的!
和昨天的题目[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)基本就是一道题了,遍历顺序也是一样一样的!
代码如下:
```CPP
@ -77,7 +77,7 @@ public:
## 周三
[动态规划322.零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数每种硬币的数量是无限的
[动态规划322.零钱兑换](https://programmercarl.com/0322.零钱兑换.html)给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数每种硬币的数量是无限的
这里我们都知道这是完全背包。
@ -137,10 +137,10 @@ public:
## 周四
[动态规划279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)给定正整数 n找到若干个完全平方数比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少平方数可以重复使用
[动态规划279.完全平方数](https://programmercarl.com/0279.完全平方数.html)给定正整数 n找到若干个完全平方数比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少平方数可以重复使用
如果按顺序把前面的文章都看了,这道题目就是简单题了。 dp[i]的定义,递推公式,初始化,遍历顺序,都是和[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ) 一样一样的。
如果按顺序把前面的文章都看了,这道题目就是简单题了。 dp[i]的定义,递推公式,初始化,遍历顺序,都是和[动态规划322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html) 一样一样的。
要是没有前面的基础上来做这道题,那这道题目就有点难度了。
@ -193,9 +193,9 @@ public:
我这里做一下总结:
求组合数:[动态规划518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)
求排列数:[动态规划377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)、[动态规划70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)
求最小数:[动态规划322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)、[动态规划279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)
求组合数:[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)
求排列数:[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)、[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)
求最小数:[动态规划322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)、[动态规划279.完全平方数](https://programmercarl.com/0279.完全平方数.html)
此时我们就已经把完全背包的遍历顺序研究的透透的了!

View File

@ -3,7 +3,7 @@
## 周一
[动态规划:开始打家劫舍!](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)中就是给一个数组相邻之间不能连着偷,如果偷才能得到最大金钱。
[动态规划:开始打家劫舍!](https://programmercarl.com/0198.打家劫舍.html)中就是给一个数组相邻之间不能连着偷,如果偷才能得到最大金钱。
1. 确定dp数组含义
@ -35,7 +35,7 @@ dp[1] = max(nums[0], nums[1]);
## 周二
[动态规划:继续打家劫舍!](https://mp.weixin.qq.com/s/kKPx4HpH3RArbRcxAVHbeQ)就是数组成环了,然后相邻的不能连着偷。
[动态规划:继续打家劫舍!](https://programmercarl.com/0213.打家劫舍II.html)就是数组成环了,然后相邻的不能连着偷。
这里主要考虑清楚三种情况:
@ -61,11 +61,11 @@ dp[1] = max(nums[0], nums[1]);
所以我在本文重点强调了情况一二三是“考虑”的范围,而具体房间偷与不偷交给递推公式去抉择。
剩下的就和[动态规划:开始打家劫舍!](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)是一个逻辑了。
剩下的就和[动态规划:开始打家劫舍!](https://programmercarl.com/0198.打家劫舍.html)是一个逻辑了。
## 周三
[动态规划:还要打家劫舍!](https://mp.weixin.qq.com/s/BOJ1lHsxbQxUZffXlgglEQ)这次是在一颗二叉树上打家劫舍了,条件还是一样的,相临的不能偷。
[动态规划:还要打家劫舍!](https://programmercarl.com/0337.打家劫舍III.html)这次是在一颗二叉树上打家劫舍了,条件还是一样的,相临的不能偷。
这道题目是树形DP的入门题目其实树形DP其实就是在树上进行递推公式的推导没有什么神秘的。
@ -184,14 +184,14 @@ return {val2, val1};
因为平时我们习惯了在一维数组或者二维数组上推导公式,一下子换成了树,就需要对树的遍历方式足够了解!
大家还记不记得我在讲解贪心专题的时候,讲到这道题目:[贪心算法:我要监控二叉树!](https://mp.weixin.qq.com/s/kCxlLLjWKaE6nifHC3UL2Q),这也是贪心算法在树上的应用。**那我也可以把这个算法起一个名字,叫做树形贪心**,哈哈哈
大家还记不记得我在讲解贪心专题的时候,讲到这道题目:[贪心算法:我要监控二叉树!](https://programmercarl.com/0968.监控二叉树.html),这也是贪心算法在树上的应用。**那我也可以把这个算法起一个名字,叫做树形贪心**,哈哈哈
“树形贪心”词汇从此诞生,来自「代码随想录」
## 周四
[动态规划:买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ) 一段时间,只能买买一次,问最大收益。
[动态规划:买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html) 一段时间,只能买买一次,问最大收益。
这里我给出了三中解法:

View File

@ -3,9 +3,9 @@
## 周一
[动态规划买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w)中股票可以买买多了次!
[动态规划买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II动态规划.html)中股票可以买买多了次!
这也是和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的唯一区别(注意只有一只股票,所以再次购买前要出售掉之前的股票)
这也是和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别(注意只有一只股票,所以再次购买前要出售掉之前的股票)
重点在于递推公式公式的不同。
@ -22,7 +22,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
```
大家可以发现本题和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的代码几乎一样,唯一的区别在:
大家可以发现本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的代码几乎一样,唯一的区别在:
```
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
@ -32,7 +32,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
## 周二
[动态规划买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)中最多只能完成两笔交易。
[动态规划买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)中最多只能完成两笔交易。
**这意味着可以买卖一次,可以买卖两次,也可以不买卖**
@ -85,9 +85,9 @@ dp[0][4] = 0;
## 周三
[动态规划买卖股票的最佳时机IV](https://mp.weixin.qq.com/s/jtxZJWAo2y5sUsW647Z5cw)最多可以完成 k 笔交易。
[动态规划买卖股票的最佳时机IV](https://programmercarl.com/0188.买卖股票的最佳时机IV.html)最多可以完成 k 笔交易。
相对于上一道[动态规划123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)本题需要通过前两次的交易来类比前k次的交易
相对于上一道[动态规划123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)本题需要通过前两次的交易来类比前k次的交易
1. 确定dp数组以及下标的含义
@ -117,7 +117,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) {
}
```
**本题和[动态规划123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)最大的区别就是这里要类比j为奇数是买偶数是卖剩的状态**
**本题和[动态规划123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)最大的区别就是这里要类比j为奇数是买偶数是卖剩的状态**
3. dp数组如何初始化
@ -147,9 +147,9 @@ for (int j = 1; j < 2 * k; j += 2) {
## 周四
[动态规划:最佳买卖股票时机含冷冻期](https://mp.weixin.qq.com/s/IgC0iWWCDpYL9ZbTHGHgfw)尽可能地完成更多的交易多次买卖一支股票但有冷冻期冷冻期为1天
[动态规划:最佳买卖股票时机含冷冻期](https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html)尽可能地完成更多的交易多次买卖一支股票但有冷冻期冷冻期为1天
相对于[动态规划122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w),本题加上了一个冷冻期
相对于[动态规划122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II动态规划.html),本题加上了一个冷冻期
**本题则需要第三个状态:不持有股票(冷冻期)的最多现金**

View File

@ -174,7 +174,7 @@ vector<int> postorderTraversal(TreeNode* root) {
```
### 广度优先遍历(队列)
相关题解:[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
相关题解:[0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html)
```
vector<vector<int>> levelOrder(TreeNode* root) {
@ -202,13 +202,13 @@ vector<vector<int>> levelOrder(TreeNode* root) {
可以直接解决如下题目:
* [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
* [0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html)
* [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md)
* [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md)
* [0104.二叉树的最大深度 (迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md)
* [0104.二叉树的最大深度 (迭代法)](https://programmercarl.com/0104.二叉树的最大深度.html)
* [0111.二叉树的最小深度(迭代法)]((https://github.com/youngyangyang04/leetcode/blob/master/problems/0111.二叉树的最小深度.md))
* [0222.完全二叉树的节点个数(迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0222.完全二叉树的节点个数.md)
* [0111.二叉树的最小深度(迭代法)](https://programmercarl.com/0111.二叉树的最小深度.html)
* [0222.完全二叉树的节点个数(迭代法)](https://programmercarl.com/0222.完全二叉树的节点个数.html)
### 二叉树深度

View File

@ -137,7 +137,7 @@ dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化
```
// 初始化 dp
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0));
for (int j = weight[0]; j <= bagWeight; j++) {
dp[0][j] = value[0];
}
@ -230,7 +230,7 @@ void test_2_wei_bag_problem1() {
int bagWeight = 4;
// 二维数组
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0));
// 初始化
for (int j = weight[0]; j <= bagWeight; j++) {

View File

@ -80,7 +80,7 @@ dp状态图如下
* [动态规划关于01背包问题你该了解这些](https://programmercarl.com/背包理论基础01背包-1.html)
* [动态规划关于01背包问题你该了解这些滚动数组](https://programmercarl.com/背包理论基础01背包-2.html)
就知道了01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了dp数组的两个for循环先后循序一定是先遍历物品再遍历背包容量。
就知道了01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了dp数组的两个for循环先后循序一定是先遍历物品再遍历背包容量。
**在完全背包中对于一维dp数组来说其实两个for循环嵌套顺序同样无所谓**