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

This commit is contained in:
programmercarl
2023-07-14 11:13:29 +08:00
41 changed files with 1450 additions and 195 deletions

View File

@ -409,7 +409,7 @@ impl Solution {
// 递归 // 递归
impl Solution { impl Solution {
pub fn swap_pairs(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> { pub fn swap_pairs(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
if head == None || head.as_ref().unwrap().next == None { if head.is_none() || head.as_ref().unwrap().next.is_none() {
return head; return head;
} }

View File

@ -240,7 +240,7 @@ class Solution {
while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换 while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换
left--; left--;
} }
// 向滑动,找右边界 // 向滑动,找右边界
while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。 while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。
right++; right++;
} }

View File

@ -98,8 +98,10 @@ public:
} }
}; };
// 时间复杂度: 最差情况所有元素都是唯一的。复杂度和全排列1都是 O(n! * n) 对于 n 个元素一共有 n! 中排列方案。而对于每一个答案,我们需要 O(n) 去复制最终放到 result 数组
// 空间复杂度: O(n) 回溯树的深度取决于我们有多少个元素
``` ```
* 时间复杂度: O(n) * 时间复杂度: O(n! * n)
* 空间复杂度: O(n) * 空间复杂度: O(n)
## 拓展 ## 拓展

View File

@ -397,7 +397,39 @@ class Solution:
``` ```
动态规划(版本五)
```python
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid):
if obstacleGrid[0][0] == 1:
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [0] * n # 创建一个一维列表用于存储路径数
# 初始化第一行的路径数
for j in range(n):
if obstacleGrid[0][j] == 1:
break
dp[j] = 1
# 计算其他行的路径数
for i in range(1, m):
if obstacleGrid[i][0] == 1:
dp[0] = 0
for j in range(1, n):
if obstacleGrid[i][j] == 1:
dp[j] = 0
continue
dp[j] += dp[j - 1]
return dp[-1] # 返回最后一个元素,即终点的路径数
```
### Go ### Go
```go ```go

View File

@ -133,12 +133,13 @@ Java
class Solution { class Solution {
public int climbStairs(int n) { public int climbStairs(int n) {
int[] dp = new int[n + 1]; int[] dp = new int[n + 1];
int m = 2; int m = 2; //有兩個物品itme1重量爲一item2重量爲二
dp[0] = 1; dp[0] = 1;
for (int i = 1; i <= n; i++) { // 遍历背包 for (int i = 1; i <= n; i++) { // 遍历背包
for (int j = 1; j <= m; j++) { //遍历物品 for (int j = 1; j <= m; j++) { //遍历物品
if (i >= j) dp[i] += dp[i - j]; if (i >= j) //當前的背包容量 大於 物品重量的時候,我們才需要記錄當前的這個裝得方法(方法數+
dp[i] += dp[i - j];
} }
} }

View File

@ -40,6 +40,8 @@ exection -> execution (插入 'u')
* 0 <= word1.length, word2.length <= 500 * 0 <= word1.length, word2.length <= 500
* word1 和 word2 由小写英文字母组成 * word1 和 word2 由小写英文字母组成
# 算法公开课
**《代码随想录》算法视频公开课:[动态规划终极绝杀! LeetCode72.编辑距离](https://www.bilibili.com/video/BV1we4y157wB/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
## 思路 ## 思路

View File

@ -243,8 +243,27 @@ class Solution {
} }
} }
``` ```
> 动态规划:版本二(使用二維數組(和卡哥思路一致),下面還有使用一維滾動數組的更優化版本)
> 动态规划:版本二 ```Java
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
int dp[][] = new int[2][2];
dp[0][0] = - prices[0];
dp[0][1] = 0;
for (int i = 1; i < len; i++){
dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], - prices[i]);
dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]);
}
return dp[(len - 1) % 2][1];
}
}
```
> 动态规划:版本二(使用一維數組)
``` java ``` java
class Solution { class Solution {
@ -271,6 +290,10 @@ class Solution {
} }
} }
``` ```
```Java
```
Python: Python:
@ -510,7 +533,37 @@ public class Solution
} }
``` ```
Rust:
> 贪心
```rust
impl Solution {
pub fn max_profit(prices: Vec<i32>) -> i32 {
let (mut low, mut res) = (i32::MAX, 0);
for p in prices {
low = p.min(low);
res = res.max(p - low);
}
res
}
}
```
> 动态规划
```rust
impl Solution {
pub fn max_profit(prices: Vec<i32>) -> i32 {
let mut dp = vec![-prices[0], 0];
for p in prices {
dp[0] = dp[0].max(-p);
dp[1] = dp[1].max(dp[0] + p);
}
dp[1]
}
}
```
<p align="center"> <p align="center">

View File

@ -322,13 +322,10 @@ function maxProfit(prices: number[]): number {
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: i32, b: i32) -> i32 {
if a > b { a } else { b }
}
pub fn max_profit(prices: Vec<i32>) -> i32 { pub fn max_profit(prices: Vec<i32>) -> i32 {
let mut result = 0; let mut result = 0;
for i in 1..prices.len() { for i in 1..prices.len() {
result += Self::max(prices[i] - prices[i - 1], 0); result += (prices[i] - prices[i - 1]).max(0);
} }
result result
} }
@ -339,18 +336,14 @@ impl Solution {
```Rust ```Rust
impl Solution { impl Solution {
fn max(a: i32, b: i32) -> i32 {
if a > b { a } else { b }
}
pub fn max_profit(prices: Vec<i32>) -> i32 { pub fn max_profit(prices: Vec<i32>) -> i32 {
let n = prices.len(); let mut dp = vec![vec![0; 2]; prices.len()];
let mut dp = vec![vec![0; 2]; n]; dp[0][0] = -prices[0];
dp[0][0] -= prices[0]; for i in 1..prices.len() {
for i in 1..n { dp[i][0] = dp[i - 1][0].max(dp[i - 1][1] - prices[i]);
dp[i][0] = Self::max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = dp[i - 1][1].max(dp[i - 1][0] + prices[i]);
dp[i][1] = Self::max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
} }
Self::max(dp[n - 1][0], dp[n - 1][1]) dp[prices.len() - 1][1]
} }
} }
``` ```

View File

@ -154,7 +154,24 @@ class Solution
} }
} }
``` ```
```java
//DP using 2*2 Array (下方還有使用一維滾動數組的更優化版本)
class Solution {
public int maxProfit(int[] prices) {
int dp[][] = new int [2][2];
//dp[i][0]: holding the stock
//dp[i][1]: not holding the stock
dp[0][0] = - prices[0];
dp[0][1] = 0;
for(int i = 1; i < prices.length; i++){
dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);
dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]);
}
return dp[(prices.length - 1) % 2][1];
}
}
```
```java ```java
// 优化空间 // 优化空间
class Solution { class Solution {
@ -346,7 +363,52 @@ public class Solution
} }
``` ```
Rust:
> 贪心
```rust
impl Solution {
pub fn max_profit(prices: Vec<i32>) -> i32 {
let mut result = 0;
for i in 1..prices.len() {
result += (prices[i] - prices[i - 1]).max(0);
}
result
}
}
```
>动态规划
```rust
impl Solution {
pub fn max_profit(prices: Vec<i32>) -> i32 {
let mut dp = vec![vec![0; 2]; prices.len()];
dp[0][0] = -prices[0];
for i in 1..prices.len() {
dp[i][0] = dp[i - 1][0].max(dp[i - 1][1] - prices[i]);
dp[i][1] = dp[i - 1][1].max(dp[i - 1][0] + prices[i]);
}
dp[prices.len() - 1][1]
}
}
```
> 优化
```rust
impl Solution {
pub fn max_profit(prices: Vec<i32>) -> i32 {
let mut dp = vec![-prices[0], 0];
for p in prices {
dp[0] = dp[0].max(dp[1] - p);
dp[1] = dp[1].max(dp[0] + p);
}
dp[1]
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -188,6 +188,54 @@ class Solution {
} }
} }
``` ```
```Java
//BFS使用helper function
class Solution {
int[][] dir ={{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
public void solve(char[][] board) {
for(int i = 0; i < board.length; i++){
if(board[i][0] == 'O') bfs(board, i, 0);
if(board[i][board[0].length - 1] == 'O') bfs(board, i, board[0].length - 1);
}
for(int j = 1 ; j < board[0].length - 1; j++){
if(board[0][j] == 'O') bfs(board, 0, j);
if(board[board.length - 1][j] == 'O') bfs(board, board.length - 1, j);
}
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
if(board[i][j] == 'O') board[i][j] = 'X';
if(board[i][j] == 'A') board[i][j] = 'O';
}
}
}
private void bfs(char[][] board, int x, int y){
Queue<Integer> que = new LinkedList<>();
board[x][y] = 'A';
que.offer(x);
que.offer(y);
while(!que.isEmpty()){
int currX = que.poll();
int currY = que.poll();
for(int i = 0; i < 4; i++){
int nextX = currX + dir[i][0];
int nextY = currY + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= board.length || nextY >= board[0].length)
continue;
if(board[nextX][nextY] == 'X'|| board[nextX][nextY] == 'A')
continue;
bfs(board, nextX, nextY);
}
}
}
}
```
```Java ```Java
// 深度优先遍历 // 深度优先遍历
// 使用 visited 数组进行标记 // 使用 visited 数组进行标记
@ -296,6 +344,47 @@ class Solution {
} }
} }
``` ```
```java
//DFS(有終止條件)
class Solution {
int[][] dir ={{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
public void solve(char[][] board) {
for(int i = 0; i < board.length; i++){
if(board[i][0] == 'O') dfs(board, i, 0);
if(board[i][board[0].length - 1] == 'O') dfs(board, i, board[0].length - 1);
}
for(int j = 1 ; j < board[0].length - 1; j++){
if(board[0][j] == 'O') dfs(board, 0, j);
if(board[board.length - 1][j] == 'O') dfs(board, board.length - 1, j);
}
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
if(board[i][j] == 'O') board[i][j] = 'X';
if(board[i][j] == 'A') board[i][j] = 'O';
}
}
}
private void dfs(char[][] board, int x, int y){
if(board[x][y] == 'X'|| board[x][y] == 'A')
return;
board[x][y] = 'A';
for(int i = 0; i < 4; i++){
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= board.length || nextY >= board[0].length)
continue;
// if(board[nextX][nextY] == 'X'|| board[nextX][nextY] == 'A')
// continue;
dfs(board, nextX, nextY);
}
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -118,7 +118,7 @@ for (int i = startIndex; i < s.size(); i++) {
continue; continue;
} }
backtracking(s, i + 1); // 寻找i+1为起始位置的子串 backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经填在的子串 path.pop_back(); // 回溯过程,弹出本次已经添加的子串
} }
``` ```
@ -189,7 +189,7 @@ private:
continue; continue;
} }
backtracking(s, i + 1); // 寻找i+1为起始位置的子串 backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经填在的子串 path.pop_back(); // 回溯过程,弹出本次已经添加的子串
} }
} }
bool isPalindrome(const string& s, int start, int end) { bool isPalindrome(const string& s, int start, int end) {
@ -245,7 +245,7 @@ private:
continue; continue;
} }
backtracking(s, i + 1); // 寻找i+1为起始位置的子串 backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经填在的子串 path.pop_back(); // 回溯过程,弹出本次已经添加的子串
} }
} }
void computePalindrome(const string& s) { void computePalindrome(const string& s) {
@ -437,7 +437,7 @@ class Solution:
substring = s[startIndex:i + 1] substring = s[startIndex:i + 1]
path.append(substring) path.append(substring)
self.backtracking(s, i + 1, path, result, isPalindrome) # 寻找i+1为起始位置的子串 self.backtracking(s, i + 1, path, result, isPalindrome) # 寻找i+1为起始位置的子串
path.pop() # 回溯过程,弹出本次已经填在的子串 path.pop() # 回溯过程,弹出本次已经添加的子串
def computePalindrome(self, s, isPalindrome): def computePalindrome(self, s, isPalindrome):
for i in range(len(s) - 1, -1, -1): # 需要倒序计算保证在i行时i+1行已经计算好了 for i in range(len(s) - 1, -1, -1): # 需要倒序计算保证在i行时i+1行已经计算好了
@ -497,7 +497,7 @@ func dfs(s string, start int) {
if isPalindrome(str) { // 是回文子串 if isPalindrome(str) { // 是回文子串
path = append(path, str) path = append(path, str)
dfs(s, i+1) // 寻找i+1为起始位置的子串 dfs(s, i+1) // 寻找i+1为起始位置的子串
path = path[:len(path)-1] // 回溯过程,弹出本次已经填在的子串 path = path[:len(path)-1] // 回溯过程,弹出本次已经添加的子串
} }
} }
} }

View File

@ -496,7 +496,24 @@ function wordBreak(s: string, wordDict: string[]): boolean {
}; };
``` ```
Rust:
```rust
impl Solution {
pub fn word_break(s: String, word_dict: Vec<String>) -> bool {
let mut dp = vec![false; s.len() + 1];
dp[0] = true;
for i in 1..=s.len() {
for j in 0..i {
if word_dict.iter().any(|word| *word == s[j..i]) && dp[j] {
dp[i] = true;
}
}
}
dp[s.len()]
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">

View File

@ -467,9 +467,57 @@ class Solution:
return " ".join(words) return " ".join(words)
``` ```
Go Go
版本一:
```go
func reverseWords(s string) string {
b := []byte(s)
// 移除前面、中间、后面存在的多余空格
slow := 0
for i := 0; i < len(b); i++ {
if b[i] != ' ' {
if slow != 0 {
b[slow] = ' '
slow++
}
for i < len(b) && b[i] != ' ' { // 复制逻辑
b[slow] = b[i]
slow++
i++
}
}
}
b = b[0:slow]
// 翻转整个字符串
reverse(b)
// 翻转每个单词
last := 0
for i := 0; i <= len(b); i++ {
if i == len(b) || b[i] == ' ' {
reverse(b[last:i])
last = i + 1
}
}
return string(b)
}
func reverse(b []byte) {
left := 0
right := len(b) - 1
for left < right {
b[left], b[right] = b[right], b[left]
left++
right--
}
}
```
版本二:
```go ```go
import ( import (
"fmt" "fmt"

View File

@ -227,7 +227,7 @@ class Solution {
} }
} }
//版本三:一维 dp数组 //版本三:一维 dp数组 (下面有和卡哥邏輯一致的一維數組JAVA解法)
class Solution { class Solution {
public int maxProfit(int k, int[] prices) { public int maxProfit(int k, int[] prices) {
if(prices.length == 0){ if(prices.length == 0){
@ -259,6 +259,41 @@ class Solution {
} }
} }
``` ```
```JAVA
class Solution {
public int maxProfit(int k, int[] prices) {
//edge cases
if(prices.length == 0 || k == 0)
return 0;
int dp[] = new int [k * 2 + 1];
//和卡哥邏輯一致,奇數天購入股票,故初始化只初始化奇數天。
for(int i = 1; i < 2 * k + 1; i += 2){
dp[i] = -prices[0];
}
for(int i = 1; i < prices.length; i++){ //i 從 1 開始,因爲第 i = 0 天已經透過初始化完成了。
for(int j = 1; j < 2 * k + 1; j++){ //j 從 1 開始,因爲第 j = 0 天已經透過初始化完成了。
//奇數天購買
if(j % 2 == 1)
dp[j] = Math.max(dp[j], dp[j - 1] - prices[i]);
//偶數天賣出
else
dp[j] = Math.max(dp[j], dp[j - 1] + prices[i]);
}
//打印DP數組
//for(int x : dp)
// System.out.print(x +", ");
//System.out.println();
}
//return 第2 * k次賣出的獲利。
return dp[2 * k];
}
}
```
Python: Python:

View File

@ -314,7 +314,24 @@ function rob(nums: number[]): number {
}; };
``` ```
Rust:
```rust
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
if nums.len() == 1 {
return nums[0];
}
let mut dp = vec![0; nums.len()];
dp[0] = nums[0];
dp[1] = nums[0].max(nums[1]);
for i in 2..nums.len() {
dp[i] = (dp[i - 2] + nums[i]).max(dp[i - 1]);
}
dp[nums.len() - 1]
}
}
```
<p align="center"> <p align="center">

View File

@ -176,6 +176,48 @@ public void dfs(char[][] grid, int i, int j){
dfs(grid,i,j + 1); dfs(grid,i,j + 1);
dfs(grid,i,j - 1); dfs(grid,i,j - 1);
} }
```
```java
//graph - dfs (和卡哥的代碼邏輯一致)
class Solution {
boolean[][] visited;
int dir[][] = {
{0, 1}, //right
{1, 0}, //down
{-1, 0}, //up
{0, -1} //left
};
public int numIslands(char[][] grid) {
int count = 0;
visited = new boolean[grid.length][grid[0].length];
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(visited[i][j] == false && grid[i][j] == '1'){
count++;
dfs(grid, i, j);
}
}
}
return count;
}
private void dfs(char[][]grid, int x, int y){
if(visited[x][y] == true || grid[x][y] == '0')
return;
visited[x][y] = true;
for(int i = 0; i < 4; i++){
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length)
continue;
dfs(grid, nextX, nextY);
}
}
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>

View File

@ -301,6 +301,35 @@ function robRange(nums: number[], start: number, end: number): number {
} }
``` ```
Rust:
```rust
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
match nums.len() {
1 => nums[0],
_ => Self::rob_range(&nums, 0, nums.len() - 2).max(Self::rob_range(
&nums,
1,
nums.len() - 1,
)),
}
}
pub fn rob_range(nums: &Vec<i32>, start: usize, end: usize) -> i32 {
if start == end {
return nums[start];
}
let mut dp = vec![0; nums.len()];
dp[start] = nums[start];
dp[start + 1] = nums[start].max(nums[start + 1]);
for i in start + 2..=end {
dp[i] = dp[i - 1].max(dp[i - 2] + nums[i]);
}
dp[end]
}
}
```
<p align="center"> <p align="center">

View File

@ -163,6 +163,7 @@ class Solution(object):
a_count = Counter(s) a_count = Counter(s)
b_count = Counter(t) b_count = Counter(t)
return a_count == b_count return a_count == b_count
```
Go Go

View File

@ -390,6 +390,8 @@ public:
```Java ```Java
//解法一 //解法一
//方式一
class Solution { class Solution {
/** /**
* 递归法 * 递归法
@ -428,9 +430,32 @@ class Solution {
} }
} }
} }
//方式二
class Solution {
List<String> result = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
deal(root, "");
return result;
}
public void deal(TreeNode node, String s) {
if (node == null)
return;
if (node.left == null && node.right == null) {
result.add(new StringBuilder(s).append(node.val).toString());
return;
}
String tmp = new StringBuilder(s).append(node.val).append("->").toString();
deal(node.left, tmp);
deal(node.right, tmp);
}
}
``` ```
```java ```java
// 解法2 // 解法
class Solution { class Solution {
/** /**
* 迭代法 * 迭代法

View File

@ -177,15 +177,19 @@ class Solution {
for (int j = 0; j <= n; j++) { for (int j = 0; j <= n; j++) {
dp[j] = max; dp[j] = max;
} }
//如果不想要寫for-loop填充數組的話也可以用JAVA內建的Arrays.fill()函數。
//Arrays.fill(dp, Integer.MAX_VALUE);
//当和为0时组合的个数为0 //当和为0时组合的个数为0
dp[0] = 0; dp[0] = 0;
// 遍历物品 // 遍历物品
for (int i = 1; i * i <= n; i++) { for (int i = 1; i * i <= n; i++) {
// 遍历背包 // 遍历背包
for (int j = i * i; j <= n; j++) { for (int j = i * i; j <= n; j++) {
if (dp[j - i * i] != max) { //if (dp[j - i * i] != max) {
dp[j] = Math.min(dp[j], dp[j - i * i] + 1); dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
} //}
//不需要這個if statement因爲在完全平方數這一題不會有"湊不成"的狀況發生( 一定可以用"1"來組成任何一個n故comment掉這個if statement。
} }
} }
return dp[n]; return dp[n];
@ -356,6 +360,7 @@ var numSquares2 = function(n) {
TypeScript TypeScript
```typescript ```typescript
// 先遍历物品
function numSquares(n: number): number { function numSquares(n: number): number {
const goodsNum: number = Math.floor(Math.sqrt(n)); const goodsNum: number = Math.floor(Math.sqrt(n));
const dp: number[] = new Array(n + 1).fill(Infinity); const dp: number[] = new Array(n + 1).fill(Infinity);
@ -370,6 +375,64 @@ function numSquares(n: number): number {
}; };
``` ```
```rust
// 先遍历背包
function numSquares(n: number): number {
const dp = Array(n + 1).fill(Infinity)
dp[0] = 0;
for(let i = 1; i <= n; i++){
for(let j = 1; j * j <= i; j++){
dp[i] = Math.min(dp[i], dp[i -j * j] + 1)
}
}
return dp[n]
};
```
Rust:
```rust
// 先遍历背包
impl Solution {
pub fn num_squares(n: i32) -> i32 {
let n = n as usize;
let mut dp = vec![i32::MAX; n + 1];
dp[0] = 0;
for i in 0..=n {
let mut j = 1;
loop {
match j * j > i {
true => break,
false => dp[i] = dp[i].min(dp[i - j * j] + 1),
}
j += 1;
}
}
dp[n]
}
}
```
```rust
// 先遍历物品
impl Solution {
pub fn num_squares(n: i32) -> i32 {
let (n, mut goods) = (n as usize, 1);
let mut dp = vec![i32::MAX; n + 1];
dp[0] = 0;
loop {
if goods * goods > n {
break;
}
for j in goods * goods..=n {
dp[j] = dp[j].min(dp[j - goods * goods] + 1);
}
goods += 1;
}
dp[n]
}
}
```
<p align="center"> <p align="center">

View File

@ -148,6 +148,8 @@ class Solution {
``` ```
Python Python
DP
```python ```python
class Solution: class Solution:
def lengthOfLIS(self, nums: List[int]) -> int: def lengthOfLIS(self, nums: List[int]) -> int:
@ -162,7 +164,31 @@ class Solution:
result = max(result, dp[i]) #取长的子序列 result = max(result, dp[i]) #取长的子序列
return result return result
``` ```
贪心
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if len(nums) <= 1:
return len(nums)
tails = [nums[0]] # 存储递增子序列的尾部元素
for num in nums[1:]:
if num > tails[-1]:
tails.append(num) # 如果当前元素大于递增子序列的最后一个元素,直接加入到子序列末尾
else:
# 使用二分查找找到当前元素在递增子序列中的位置,并替换对应位置的元素
left, right = 0, len(tails) - 1
while left < right:
mid = (left + right) // 2
if tails[mid] < num:
left = mid + 1
else:
right = mid
tails[left] = num
return len(tails) # 返回递增子序列的长度
```
Go Go
```go ```go
// 动态规划求解 // 动态规划求解

View File

@ -200,7 +200,33 @@ class Solution {
} }
} }
``` ```
```java
//using 2*4 array for space optimization
//這裡稍微說一下我在LeetCode提交的時候2*4 2-D array的performance基本上和下面的1-D array performance差不多
//都是time: 1ms, space: 40.X MB (其實 length*4 的 2-D array也僅僅是space:41.X MB看起來不多
//股票累的DP題目大致上都是這樣就當作是一個延伸就好了。真的有人問如何優化最起碼有東西可以講。
class Solution {
/**
1. [i][0] holding the stock
2. [i][1] after cooldown but stil not buing the stock
3. [i][2] selling the stock
4. [i][3] cooldown
*/
public int maxProfit(int[] prices) {
int len = prices.length;
int dp[][] = new int [2][4];
dp[0][0] = -prices[0];
for(int i = 1; i < len; i++){
dp[i % 2][0] = Math.max(Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]), dp[(i - 1) % 2][3] - prices[i]);
dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][3]);
dp[i % 2][2] = dp[(i - 1) % 2][0] + prices[i];
dp[i % 2][3] = dp[(i - 1) % 2][2];
}
return Math.max(Math.max(dp[(len - 1) % 2][1], dp[(len - 1) % 2][2]), dp[(len - 1) % 2][3]);
}
}
```
```java ```java
// 一维数组优化 // 一维数组优化
class Solution { class Solution {
@ -248,23 +274,51 @@ class Solution {
``` ```
Python Python
版本一
```python ```python
from typing import List
class Solution: class Solution:
def maxProfit(self, prices: List[int]) -> int: def maxProfit(self, prices: List[int]) -> int:
n = len(prices) n = len(prices)
if n == 0: if n == 0:
return 0 return 0
dp = [[0] * 4 for _ in range(n)] dp = [[0] * 4 for _ in range(n)] # 创建动态规划数组4个状态分别表示持有股票、不持有股票且处于冷冻期、不持有股票且不处于冷冻期、不持有股票且当天卖出后处于冷冻期
dp[0][0] = -prices[0] #持股票 dp[0][0] = -prices[0] # 初始状态:第一天持有股票的最大利润为买入股票的价格
for i in range(1, n): for i in range(1, n):
dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) # 当前持有股票的最大利润等于前一天持有股票的最大利润或者前一天不持有股票且不处于冷冻期的最大利润减去当前股票的价格
dp[i][1] = max(dp[i-1][1], dp[i-1][3]) dp[i][1] = max(dp[i-1][1], dp[i-1][3]) # 当前不持有股票且处于冷冻期的最大利润等于前一天持有股票的最大利润加上当前股票的价格
dp[i][2] = dp[i-1][0] + prices[i] dp[i][2] = dp[i-1][0] + prices[i] # 当前不持有股票且不处于冷冻期的最大利润等于前一天不持有股票的最大利润或者前一天处于冷冻期的最大利润
dp[i][3] = dp[i-1][2] dp[i][3] = dp[i-1][2] # 当前不持有股票且当天卖出后处于冷冻期的最大利润等于前一天不持有股票且不处于冷冻期的最大利润
return max(dp[n-1][3], dp[n-1][1], dp[n-1][2]) return max(dp[n-1][3], dp[n-1][1], dp[n-1][2]) # 返回最后一天不持有股票的最大利润
```
```
版本二
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2:
return 0
# 定义三种状态的动态规划数组
dp = [[0] * 3 for _ in range(n)]
dp[0][0] = -prices[0] # 持有股票的最大利润
dp[0][1] = 0 # 不持有股票,且处于冷冻期的最大利润
dp[0][2] = 0 # 不持有股票,不处于冷冻期的最大利润
for i in range(1, n):
# 当前持有股票的最大利润等于前一天持有股票的最大利润或者前一天不持有股票且不处于冷冻期的最大利润减去当前股票的价格
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
# 当前不持有股票且处于冷冻期的最大利润等于前一天持有股票的最大利润加上当前股票的价格
dp[i][1] = dp[i-1][0] + prices[i]
# 当前不持有股票且不处于冷冻期的最大利润等于前一天不持有股票的最大利润或者前一天处于冷冻期的最大利润
dp[i][2] = max(dp[i-1][2], dp[i-1][1])
# 返回最后一天不持有股票的最大利润
return max(dp[-1][1], dp[-1][2])
```
Go Go
```go ```go
// 最佳买卖股票时机含冷冻期 动态规划 // 最佳买卖股票时机含冷冻期 动态规划

View File

@ -355,23 +355,52 @@ func min(a, b int) int {
Rust: Rust:
```rust ```rust
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 { // 遍历物品
impl Solution {
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {
let amount = amount as usize; let amount = amount as usize;
let mut dp = vec![i32::MAX; amount + 1]; let mut dp = vec![i32::MAX; amount + 1];
dp[0] = 0; dp[0] = 0;
for i in 0..coins.len() { for coin in coins {
for j in coins[i] as usize..=amount { for i in coin as usize..=amount {
if dp[j - coins[i] as usize] != i32::MAX { if dp[i - coin as usize] != i32::MAX {
dp[j] = dp[j].min(dp[j - coins[i] as usize] + 1); dp[i] = dp[i].min(dp[i - coin as usize] + 1);
} }
} }
} }
if dp[amount] == i32::MAX { -1 } else { dp[amount] } if dp[amount] == i32::MAX {
return -1;
}
dp[amount]
}
}
```
```rust
// 遍历背包
impl Solution {
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {
let amount = amount as usize;
let mut dp = vec![i32::MAX; amount + 1];
dp[0] = 0;
for i in 1..=amount {
for &coin in &coins {
if i >= coin as usize && dp[i - coin as usize] != i32::MAX {
dp[i] = dp[i].min(dp[i - coin as usize] + 1)
}
}
}
if dp[amount] == i32::MAX {
return -1;
}
dp[amount]
}
} }
``` ```
Javascript Javascript
```javascript ```javascript
// 遍历物品
const coinChange = (coins, amount) => { const coinChange = (coins, amount) => {
if(!amount) { if(!amount) {
return 0; return 0;
@ -380,7 +409,7 @@ const coinChange = (coins, amount) => {
let dp = Array(amount + 1).fill(Infinity); let dp = Array(amount + 1).fill(Infinity);
dp[0] = 0; dp[0] = 0;
for(let i =0; i < coins.length; i++) { for(let i = 0; i < coins.length; i++) {
for(let j = coins[i]; j <= amount; j++) { for(let j = coins[i]; j <= amount; j++) {
dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]); dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
} }
@ -390,9 +419,26 @@ const coinChange = (coins, amount) => {
} }
``` ```
```javascript
// 遍历背包
var coinChange = function(coins, amount) {
const dp = Array(amount + 1).fill(Infinity)
dp[0] = 0
for (let i = 1; i <= amount; i++) {
for (let j = 0; j < coins.length; j++) {
if (i >= coins[j] && dp[i - coins[j]] !== Infinity) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount]
}
```
TypeScript TypeScript
```typescript ```typescript
// 遍历物品
function coinChange(coins: number[], amount: number): number { function coinChange(coins: number[], amount: number): number {
const dp: number[] = new Array(amount + 1).fill(Infinity); const dp: number[] = new Array(amount + 1).fill(Infinity);
dp[0] = 0; dp[0] = 0;
@ -406,6 +452,23 @@ function coinChange(coins: number[], amount: number): number {
}; };
``` ```
```typescript
// 遍历背包
function coinChange(coins: number[], amount: number): number {
const dp: number[] = Array(amount + 1).fill(Infinity)
dp[0] = 0
for (let i = 1; i <= amount; i++) {
for (let j = 0; j < coins.length; j++) {
if (i >= coins[j] && dp[i - coins[j]] !== Infinity) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount]
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>

View File

@ -490,6 +490,33 @@ function robNode(node: TreeNode | null): MaxValueArr {
} }
``` ```
### Rust
动态规划:
```rust
use std::cell::RefCell;
use std::rc::Rc;
impl Solution {
pub fn rob(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
let (v1, v2) = Self::rob_tree(&root);
v1.max(v2)
}
pub fn rob_tree(cur: &Option<Rc<RefCell<TreeNode>>>) -> (i32, i32) {
match cur {
None => (0, 0),
Some(node) => {
let left = Self::rob_tree(&node.borrow_mut().left);
let right = Self::rob_tree(&node.borrow_mut().right);
(
left.0.max(left.1) + right.0.max(right.1), // 偷左右节点
node.borrow().val + left.0 + right.0, // 偷父节点
)
}
}
}
}
```
<p align="center"> <p align="center">

View File

@ -262,10 +262,11 @@ class Solution:
# 计算切割点j和剩余部分(i-j)的乘积,并与之前的结果进行比较取较大值 # 计算切割点j和剩余部分(i-j)的乘积,并与之前的结果进行比较取较大值
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)) dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j)
return dp[n] # 返回最终的计算结果 return dp[n] # 返回最终的计算结果
``` ```
动态规划版本二 动态规划版本二
```python ```python

View File

@ -146,6 +146,7 @@ class Solution {
``` ```
Python
(版本一)使用数组 (版本一)使用数组
```python ```python
class Solution: class Solution:
@ -210,7 +211,6 @@ class Solution:
class Solution: class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool: def canConstruct(self, ransomNote: str, magazine: str) -> bool:
return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote)) return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote))
``` ```
Go Go

View File

@ -383,6 +383,21 @@ class Solution:
return True return True
return False return False
```
卡哥版(简化版)
```python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 != 0:
return False
target = sum(nums) // 2
dp = [0] * (target + 1)
for num in nums:
for j in range(target, num-1, -1):
dp[j] = max(dp[j], dp[j-num] + num)
return dp[-1] == target
``` ```
二维DP版 二维DP版
```python ```python

View File

@ -290,26 +290,21 @@ int findMinArrowShots(int** points, int pointsSize, int* pointsColSize){
### Rust ### Rust
```Rust ```Rust
use std::cmp;
impl Solution { impl Solution {
pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 { pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 {
if points.is_empty() { if points.is_empty() {
return 0; return 0;
} }
points.sort_by_key(|point| point[0]); points.sort_by_key(|point| point[0]);
let mut result = 1;
let size = points.len(); for i in 1..points.len() {
let mut count = 1; if points[i][0] > points[i - 1][1] {
result += 1;
for i in 1..size {
if points[i][0] > points[i-1][1] {
count += 1;
} else { } else {
points[i][1] = cmp::min(points[i][1], points[i-1][1]); points[i][1] = points[i][1].min(points[i - 1][1])
} }
} }
result
return count;
} }
} }
``` ```

View File

@ -292,6 +292,85 @@ class Solution {
} }
``` ```
易于理解的二维数组版本:
```java
class Solution {
public int findTargetSumWays(int[] nums, int target) {
// 01背包应用之“有多少种不同的填满背包最大容量的方法“
// 易于理解的二维数组解法及详细注释
int sum = 0;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
}
// 注意nums[i] >= 0的题目条件意味着sum也是所有nums[i]的绝对值之和
// 这里保证了sum + target一定是大于等于零的也就是left大于等于零毕竟我们定义left大于right
if(sum < Math.abs(target)){
return 0;
}
// 利用二元一次方程组将left用target和sum表示出来替换掉right组合详见代码随想录对此题的分析
// 如果所求的left数组和为小数则作为整数数组的nums里的任何元素自然是没有办法凑出这个小数的
if((sum + target) % 2 != 0) {
return 0;
}
int left = (sum + target) / 2;
// dp[i][j]遍历到数组第i个数时 left为j时的能装满背包的方法总数
int[][] dp = new int[nums.length][left + 1];
// 初始化最上行dp[0][j])当nums[0] == j时注意nums[0]和j都一定是大于等于零的因此不需要判断等于-j时的情况有唯一一种取法可取到jdp[0][j]此时等于1
// 其他情况dp[0][j] = 0
// java整数数组默认初始值为0
for(int j = 0; j <= left; j++) {
if(nums[0] == j) {
dp[0][j] = 1;
}
}
// 初始化最左列dp[i][0])
// 当从nums数组的索引0到i的部分有n个0时n > 0)每个0可以取+/-因此有2的n次方中可以取到j = 0的方案
// n = 0说明当前遍历到的数组部分没有0全为正数因此只有一种方案可以取到j = 0就是所有数都不取
int numZeros = 0;
for(int i = 0; i < nums.length; i++) {
if(nums[i] == 0) {
numZeros++;
}
dp[i][0] = (int) Math.pow(2, numZeros);
}
// 递推公式分析:
// 当nums[i] > j时这时候nums[i]一定不能取所以是dp[i - 1][j]种方案数
// nums[i] <= j时num[i]可取可不取因此方案数是dp[i - 1][j] + dp[i - 1][j - nums[i]]
// 由递推公式可知先遍历i或j都可
for(int i = 1; i < nums.length; i++) {
for(int j = 1; j <= left; j++) {
if(nums[i] > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
}
}
}
// 打印dp数组
// for(int i = 0; i < nums.length; i++) {
// for(int j = 0; j <= left; j++) {
// System.out.print(dp[i][j] + " ");
// }
// System.out.println("");
// }
return dp[nums.length - 1][left];
}
}
```
### Python ### Python
回溯版 回溯版
```python ```python

View File

@ -16,8 +16,13 @@
* 输出: 2 * 输出: 2
* 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea" * 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
## 思路
# 算法公开课
**《代码随想录》算法视频公开课:[动态规划之子序列,还是为了编辑距离做铺垫 | LeetCode583.两个字符串的删除操(https://www.bilibili.com/video/BV1we4y157wB/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
## 思路
### 动态规划一 ### 动态规划一
本题和[动态规划115.不同的子序列](https://programmercarl.com/0115.不同的子序列.html)相比,其实就是两个字符串都可以删除了,情况虽说复杂一些,但整体思路是不变的。 本题和[动态规划115.不同的子序列](https://programmercarl.com/0115.不同的子序列.html)相比,其实就是两个字符串都可以删除了,情况虽说复杂一些,但整体思路是不变的。
@ -184,6 +189,31 @@ class Solution {
} }
} }
``` ```
```java
//DP - longest common subsequence (用最長公共子序列反推)
class Solution {
public int minDistance(String word1, String word2) {
char[] char1 = word1.toCharArray();
char[] char2 = word2.toCharArray();
int len1 = char1.length;
int len2 = char2.length;
int dp[][] = new int [len1 + 1][len2 + 1];
for(int i = 1; i <= len1; i++){
for(int j = 1; j <= len2; j++){
if(char1[i - 1] == char2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
return len1 + len2 - (2 * dp[len1][len2]);//和leetcode 1143只差在這一行。
}
}
```
Python Python

View File

@ -304,7 +304,38 @@ class Solution {
} }
} }
``` ```
LeetCode 5. Longest Palindromic Substring(LeetCode 647. 同一題的思路改一下、加一點就能通過LeetCode 5)
```java
class Solution {
public String longestPalindrome(String s) {
//題目要求要return 最長的回文連續子串,故需要記錄當前最長的連續回文子串長度、最終起點、最終終點。
int finalStart = 0;
int finalEnd = 0;
int finalLen = 0;
char[] chars = s.toCharArray();
int len = chars.length;
boolean[][] dp = new boolean[len][len];
for (int i = len - 1; i >= 0; i--) {
for (int j = i; j < len; j++) {
if (chars[i] == chars[j] && (j - i <= 1 || dp[i + 1][j - 1]))
dp[i][j] = true;
//和LeetCode 647差別就在這個if statement。
//如果當前[i, j]範圍內的substring是回文子串(dp[i][j]) 且(&&) 長度大於當前要記錄的最終長度(j - i + 1 > finalLen)
//我們就更新 當前最長的連續回文子串長度、最終起點、最終終點
if (dp[i][j] && j - i + 1 > finalLen) {
finalLen = j - i + 1;
finalStart = i;
finalEnd = j;
}
}
}
//String.substring這個method的用法是[起點, 終點),包含起點,不包含終點(左閉右開區間),故終點 + 1。
return s.substring(finalStart, finalEnd + 1);
}
}
```
Python Python

View File

@ -177,6 +177,7 @@ Java
dp[i] = 1; dp[i] = 1;
} }
int res = 1; int res = 1;
//可以注意到,這邊的 i 是從 0 開始所以會出現和卡哥的C++ code有差異的地方在一些地方會看到有 i + 1 的偏移。
for (int i = 0; i < nums.length - 1; i++) { for (int i = 0; i < nums.length - 1; i++) {
if (nums[i + 1] > nums[i]) { if (nums[i + 1] > nums[i]) {
dp[i + 1] = dp[i] + 1; dp[i + 1] = dp[i] + 1;
@ -208,7 +209,7 @@ public static int findLengthOfLCIS(int[] nums) {
Python Python
> 动态规划: DP
```python ```python
class Solution: class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int: def findLengthOfLCIS(self, nums: List[int]) -> int:
@ -223,8 +224,27 @@ class Solution:
return result return result
``` ```
DP(优化版)
```python
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
if not nums:
return 0
> 贪心法: max_length = 1
current_length = 1
for i in range(1, len(nums)):
if nums[i] > nums[i - 1]:
current_length += 1
max_length = max(max_length, current_length)
else:
current_length = 1
return max_length
```
贪心
```python ```python
class Solution: class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int: def findLengthOfLCIS(self, nums: List[int]) -> int:

View File

@ -189,6 +189,135 @@ public:
``` ```
# 其它语言版本 # 其它语言版本
## Java
### DFS
```java
// DFS
class Solution {
int[][] dir = {
{0, 1}, //right
{1, 0}, //down
{0, -1}, //left
{-1, 0} //up
};
boolean visited[][];
int count;
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
visited = new boolean[grid.length][grid[0].length];
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(visited[i][j] == false && grid[i][j] == 1){
count = 0;
dfs(grid, i, j);
res = Math.max(res, count);
}
}
}
return res;
}
private void dfs(int[][] grid, int x, int y){
if(visited[x][y] == true || grid[x][y] == 0)
return;
visited[x][y] = true;
count++;
for(int i = 0; i < 4; i++){
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length)
continue;
dfs(grid, nextX, nextY);
}
}
}
```
### BFS
```java
//BFS
class Solution {
int[][] dir = {
{0, 1}, {1, 0}, {0, -1}, {-1, 0}
};
int count;
boolean visited[][];
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
visited = new boolean[grid.length][grid[0].length];
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(visited[i][j] == false && grid[i][j] == 1){
count = 0;
bfs(grid, i, j);
res = Math.max(res, count);
}
}
}
return res;
}
private void bfs(int[][] grid, int x, int y){
Queue<Integer> que = new LinkedList<>();
que.offer(x);
que.offer(y);
visited[x][y] = true;
count++;
while(!que.isEmpty()){
int currX = que.poll();
int currY = que.poll();
for(int i = 0; i < 4; i++){
int nextX = currX + dir[i][0];
int nextY = currY + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length)
continue;
if(visited[nextX][nextY] == false && grid[nextX][nextY] == 1){
que.offer(nextX);
que.offer(nextY);
visited[nextX][nextY] = true;
count++;
}
}
}
}
}
```
### DFS 優化(遇到島嶼後,就把他淹沒)
```java
//这里使用深度优先搜索 DFS 来完成本道题目。我们使用 DFS 计算一个岛屿的面积,同时维护计算过的最大的岛屿面积。同时,为了避免对岛屿重复计算,我们在 DFS 的时候对岛屿进行 “淹没” 操作,即将岛屿所占的地方置为 0。
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
for(int i = 0;i < grid.length;i++){
for(int j = 0;j < grid[0].length;j++){
//每遇到一个岛屿就计算这个岛屿的面积同时”淹没“这个岛屿
if(grid[i][j] == 1){
//每次计算一个岛屿的面积都要与res比较维护最大的岛屿面积作为最后的答案
res = Math.max(res,dfs(grid,i,j));
}
}
}
return res;
}
public int dfs(int[][] grid,int i,int j){
//搜索边界ij超过grid的范围或者当前元素为0即当前所在的地方已经是海洋
if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == 0) return 0;
//淹没土地,防止后续被重复计算
grid[i][j] = 0;
//递归的思路:要求当前土地(i,j)所在的岛屿的面积则等于1加上下左右相邻的土地的总面积
return 1 + dfs(grid,i - 1,j) +
dfs(grid,i + 1,j) +
dfs(grid,i,j + 1) +
dfs(grid,i,j - 1);
}
```
## Python ## Python
### BFS ### BFS
@ -261,39 +390,6 @@ class Solution:
if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]): if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]):
self.dfs(grid, visited, new_x, new_y) self.dfs(grid, visited, new_x, new_y)
``` ```
## Java
这里使用深度优先搜索 DFS 来完成本道题目。我们使用 DFS 计算一个岛屿的面积,同时维护计算过的最大的岛屿面积。同时,为了避免对岛屿重复计算,我们在 DFS 的时候对岛屿进行 “淹没” 操作,即将岛屿所占的地方置为 0。
```java
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
for(int i = 0;i < grid.length;i++){
for(int j = 0;j < grid[0].length;j++){
//每遇到一个岛屿就计算这个岛屿的面积同时”淹没“这个岛屿
if(grid[i][j] == 1){
//每次计算一个岛屿的面积都要与res比较维护最大的岛屿面积作为最后的答案
res = Math.max(res,dfs(grid,i,j));
}
}
}
return res;
}
public int dfs(int[][] grid,int i,int j){
//搜索边界ij超过grid的范围或者当前元素为0即当前所在的地方已经是海洋
if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == 0) return 0;
//淹没土地,防止后续被重复计算
grid[i][j] = 0;
//递归的思路:要求当前土地(i,j)所在的岛屿的面积则等于1加上下左右相邻的土地的总面积
return 1 + dfs(grid,i - 1,j) +
dfs(grid,i + 1,j) +
dfs(grid,i,j + 1) +
dfs(grid,i,j - 1);
}
```
<p align="center"> <p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank"> <a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/> <img src="../pics/网站星球宣传海报.jpg" width="1000"/>

View File

@ -667,100 +667,128 @@ Go
//单链表实现 //单链表实现
package main package main
import "fmt" import (
"fmt"
func main() { )
var list = new(SingleLinkedList)
list.Init()
list.addAtHead(100)
list.addAtTail(242)
list.addAtTail(777)
list.addAtIndex(1, 99999)
list.printLinkedList()
}
// 单链表写法 //
type SingleNode struct { type SingleNode struct {
Val int Val int // 节点的值
Next *SingleNode Next *SingleNode // 下一个节点的指针
} }
type SingleLinkedList struct { type MyLinkedList struct {
dummyHead *SingleNode dummyHead *SingleNode // 虚拟头节点
Size int Size int // 链表大小
} }
// 初始化链表 func main() {
func (list *SingleLinkedList) Init() *SingleLinkedList { list := Constructor() // 初始化链表
list.Size = 0 list.AddAtHead(100) // 在头部添加元素
list.dummyHead = new(SingleNode) list.AddAtTail(242) // 在尾部添加元素
return list list.AddAtTail(777) // 在尾部添加元素
list.AddAtIndex(1, 99999) // 在指定位置添加元素
list.printLinkedList() // 打印链表
} }
// 获取第index个节点数值 /** Initialize your data structure here. */
func (list *SingleLinkedList) get(index int) int { func Constructor() MyLinkedList {
if list != nil || index < 0 || index > list.Size { newNode := &SingleNode{ // 创建新节点
-999,
nil,
}
return MyLinkedList{ // 返回链表
dummyHead: newNode,
Size: 0,
}
}
/** Get the value of the index-th node in the linked list. If the index is
invalid, return -1. */
func (this *MyLinkedList) Get(index int) int {
/*if this != nil || index < 0 || index > this.Size {
return -1
}*/
if this == nil || index < 0 || index >= this.Size { // 如果索引无效则返回-1
return -1 return -1
} }
// 让cur等于真正头节点 // 让cur等于真正头节点
cur := list.dummyHead.Next cur := this.dummyHead.Next // 设置当前节点为真实头节点
for i := 0; i < index; i++ { for i := 0; i < index; i++ { // 遍历到索引所在的节点
cur = cur.Next cur = cur.Next
} }
return cur.Val return cur.Val // 返回节点值
} }
// 在链表最前面插入一个节点 /** Add a node of value val before the first element of the linked list. After
func (list *SingleLinkedList) addAtHead(val int) { the insertion, the new node will be the first node of the linked list. */
func (this *MyLinkedList) AddAtHead(val int) {
// 以下两行代码可用一行代替 // 以下两行代码可用一行代替
// newNode := new(SingleNode) // newNode := new(SingleNode)
// newNode.Val = val // newNode.Val = val
newNode := &SingleNode{Val: val} newNode := &SingleNode{Val: val} // 创建新节点
newNode.Next = this.dummyHead.Next // 新节点指向当前头节点
newNode.Next = list.dummyHead.Next this.dummyHead.Next = newNode // 新节点变为头节点
list.dummyHead.Next = newNode this.Size++ // 链表大小增加1
list.Size++
} }
// 在链表最后面插入一个节点 /** Append a node of value val to the last element of the linked list. */
func (list *SingleLinkedList) addAtTail(val int) { func (this *MyLinkedList) AddAtTail(val int) {
newNode := &SingleNode{Val: val} newNode := &SingleNode{Val: val} // 创建新节点
cur := list.dummyHead cur := this.dummyHead // 设置当前节点为虚拟头节点
for cur.Next != nil { for cur.Next != nil { // 遍历到最后一个节点
cur = cur.Next cur = cur.Next
} }
cur.Next = newNode cur.Next = newNode // 在尾部添加新节点
list.Size++ this.Size++ // 链表大小增加1
} }
// 打印链表 /** Add a node of value val before the index-th node in the linked list. If
func (list *SingleLinkedList) printLinkedList() { index equals to the length of linked list, the node will be appended to the
cur := list.dummyHead end of linked list. If index is greater than the length, the node will not be
for cur.Next != nil { inserted. */
fmt.Println(cur.Next.Val) func (this *MyLinkedList) AddAtIndex(index int, val int) {
cur = cur.Next if index < 0 { // 如果索引小于0设置为0
}
}
// 在第index个节点之前插入新节点
func (list *SingleLinkedList) addAtIndex(index int, val int) {
if index < 0 {
index = 0 index = 0
} else if index > list.Size { } else if index > this.Size { // 如果索引大于链表长度,直接返回
return return
} }
newNode := &SingleNode{Val: val} newNode := &SingleNode{Val: val} // 创建新节点
cur := list.dummyHead //用虚拟头节点不用考虑在头部插入的情况 cur := this.dummyHead // 设置当前节点为虚拟头节点
for i := 0; i < index; i++ { for i := 0; i < index; i++ { // 遍历到指定索引的前一个节点
cur = cur.Next cur = cur.Next
} }
newNode.Next = cur.Next newNode.Next = cur.Next // 新节点指向原索引节点
cur.Next = newNode cur.Next = newNode // 原索引的前一个节点指向新节点
list.Size++ this.Size++ // 链表大小增加1
} }
/** Delete the index-th node in the linked list, if the index is valid. */
func (this *MyLinkedList) DeleteAtIndex(index int) {
if index < 0 || index >= this.Size { // 如果索引无效则直接返回
return
}
cur := this.dummyHead // 设置当前节点为虚拟头节点
for i := 0; i < index; i++ { // 遍历到要删除节点的前一个节点
cur = cur.Next
}
if cur.Next != nil {
cur.Next = cur.Next.Next // 当前节点直接指向下下个节点,即删除了下一个节点
}
this.Size-- // 注意删除节点后应将链表大小减一
}
// 打印链表
func (list *MyLinkedList) printLinkedList() {
cur := list.dummyHead // 设置当前节点为虚拟头节点
for cur.Next != nil { // 遍历链表
fmt.Println(cur.Next.Val) // 打印节点值
cur = cur.Next // 切换到下一个节点
}
}
``` ```
```go ```go

View File

@ -154,6 +154,25 @@ class Solution {
return dp[1]; return dp[1];
} }
} }
```Java
//使用 2*2 array
class Solution {
public int maxProfit(int[] prices, int fee) {
int dp[][] = new int[2][2];
int len = prices.length;
//[i][0] = holding the stock
//[i][1] = not holding the stock
dp[0][0] = -prices[0];
for(int i = 1; i < len; i++){
dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);
dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i] - fee);
}
return dp[(len - 1) % 2][1];
}
}
```
``` ```
Python Python

View File

@ -302,37 +302,99 @@ class Solution {
Python Python
> 动态规划: 2维DP
```python ```python
class Solution: class Solution:
def findLength(self, A: List[int], B: List[int]) -> int: def findLength(self, nums1: List[int], nums2: List[int]) -> int:
dp = [[0] * (len(B)+1) for _ in range(len(A)+1)] # 创建一个二维数组 dp用于存储最长公共子数组的长度
dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]
# 记录最长公共子数组的长度
result = 0 result = 0
for i in range(1, len(A)+1):
for j in range(1, len(B)+1): # 遍历数组 nums1
if A[i-1] == B[j-1]: for i in range(1, len(nums1) + 1):
dp[i][j] = dp[i-1][j-1] + 1 # 遍历数组 nums2
result = max(result, dp[i][j]) for j in range(1, len(nums2) + 1):
# 如果 nums1[i-1] 和 nums2[j-1] 相等
if nums1[i - 1] == nums2[j - 1]:
# 在当前位置上的最长公共子数组长度为前一个位置上的长度加一
dp[i][j] = dp[i - 1][j - 1] + 1
# 更新最长公共子数组的长度
if dp[i][j] > result:
result = dp[i][j]
# 返回最长公共子数组的长度
return result return result
``` ```
> 动态规划:滚动数组 1维DP
```python ```python
class Solution: class Solution:
def findLength(self, A: List[int], B: List[int]) -> int: def findLength(self, nums1: List[int], nums2: List[int]) -> int:
dp = [0] * (len(B) + 1) # 创建一个一维数组 dp用于存储最长公共子数组的长度
dp = [0] * (len(nums2) + 1)
# 记录最长公共子数组的长度
result = 0 result = 0
for i in range(1, len(A)+1):
for j in range(len(B), 0, -1): # 遍历数组 nums1
if A[i-1] == B[j-1]: for i in range(1, len(nums1) + 1):
dp[j] = dp[j-1] + 1 # 用于保存上一个位置的值
prev = 0
# 遍历数组 nums2
for j in range(1, len(nums2) + 1):
# 保存当前位置的值,因为会在后面被更新
current = dp[j]
# 如果 nums1[i-1] 和 nums2[j-1] 相等
if nums1[i - 1] == nums2[j - 1]:
# 在当前位置上的最长公共子数组长度为上一个位置的长度加一
dp[j] = prev + 1
# 更新最长公共子数组的长度
if dp[j] > result:
result = dp[j]
else: else:
dp[j] = 0 #注意这里不相等的时候要有赋0的操作 # 如果不相等,将当前位置的值置为零
result = max(result, dp[j]) dp[j] = 0
# 更新 prev 变量为当前位置的值,供下一次迭代使用
prev = current
# 返回最长公共子数组的长度
return result return result
``` ```
2维DP 扩展
```python
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
# 创建一个二维数组 dp用于存储最长公共子数组的长度
dp = [[0] * (len(nums2) + 1) for _ in range(len(nums1) + 1)]
# 记录最长公共子数组的长度
result = 0
# 对第一行和第一列进行初始化
for i in range(len(nums1)):
if nums1[i] == nums2[0]:
dp[i + 1][1] = 1
for j in range(len(nums2)):
if nums1[0] == nums2[j]:
dp[1][j + 1] = 1
# 填充dp数组
for i in range(1, len(nums1) + 1):
for j in range(1, len(nums2) + 1):
if nums1[i - 1] == nums2[j - 1]:
# 如果 nums1[i-1] 和 nums2[j-1] 相等,则当前位置的最长公共子数组长度为左上角位置的值加一
dp[i][j] = dp[i - 1][j - 1] + 1
if dp[i][j] > result:
# 更新最长公共子数组的长度
result = dp[i][j]
# 返回最长公共子数组的长度
return result
```
Go Go
```Go ```Go
func findLength(A []int, B []int) int { func findLength(A []int, B []int) int {

View File

@ -282,7 +282,35 @@ class Solution:
return dp1 # 返回到达楼顶的最小花费 return dp1 # 返回到达楼顶的最小花费
``` ```
动态规划(版本三)
```python
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
dp = [0] * len(cost)
dp[0] = cost[0] # 第一步有花费
dp[1] = cost[1]
for i in range(2, len(cost)):
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
# 注意最后一步可以理解为不用花费,所以取倒数第一步,第二步的最少值
return min(dp[-1], dp[-2])
```
动态规划(版本四)
```python
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost)
prev_1 = cost[0] # 前一步的最小花费
prev_2 = cost[1] # 前两步的最小花费
for i in range(2, n):
current = min(prev_1, prev_2) + cost[i] # 当前位置的最小花费
prev_1, prev_2 = prev_2, current # 更新前一步和前两步的最小花费
return min(prev_1, prev_2) # 最后一步可以理解为不用花费,取倒数第一步和第二步的最少值
```
### Go ### Go
```Go ```Go
func minCostClimbingStairs(cost []int) int { func minCostClimbingStairs(cost []int) int {

View File

@ -195,9 +195,9 @@ var largestSumAfterKNegations = function(nums, k) {
nums[nums.length-1] = - nums[nums.length-1] nums[nums.length-1] = - nums[nums.length-1]
k--; k--;
} }
return nums.reduce((a, b) => {
a + b // 使用箭头函数的隐式返回值时,需使用简写省略花括号,否则要在 a + b 前加上 return
}) return nums.reduce((a, b) => a + b)
}; };
// 版本二 (优化: 一次遍历) // 版本二 (优化: 一次遍历)

View File

@ -149,7 +149,63 @@ public:
### Java ### Java
深度优先遍历版本: 深度优先遍历(没有终止条件 + 空間優化淹沒島嶼沒有使用visited數組)
```java
//DFS
class Solution {
int count = 0;
int[][] dir ={
{0, 1},
{1, 0},
{-1, 0},
{0, -1}
};
private void dfs(int[][] grid, int x, int y){
if(grid[x][y] == 0)
return;
grid[x][y] = 0;
count++;
for(int i = 0; i < 4; i++){
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length)
continue;
dfs(grid, nextX, nextY);
}
}
public int numEnclaves(int[][] grid) {
for(int i = 0; i < grid.length; i++){
if(grid[i][0] == 1)
dfs(grid, i, 0);
if(grid[i][grid[0].length - 1] == 1)
dfs(grid, i, grid[0].length - 1);
}
//初始化的時候j 的上下限有調整過,必免重複操作。
for(int j = 1; j < grid[0].length - 1; j++){
if(grid[0][j] == 1)
dfs(grid, 0, j);
if(grid[grid.length - 1][j] == 1)
dfs(grid, grid.length - 1, j);
}
count = 0;
for(int i = 1; i < grid.length - 1; i++){
for(int j = 1; j < grid[0].length - 1; j++){
if(grid[i][j] == 1)
dfs(grid, i, j);
}
}
return count;
}
}
```
深度优先遍历(没有终止条件)
```java ```java
class Solution { class Solution {
@ -206,7 +262,7 @@ class Solution {
} }
``` ```
广度优先遍历版本: 广度优先遍历使用visited數組
```java ```java
class Solution { class Solution {
@ -269,6 +325,72 @@ class Solution {
} }
``` ```
廣度优先遍历(空間優化淹沒島嶼沒有使用visited數組)
```java
//BFS
class Solution {
int count = 0;
int[][] dir ={
{0, 1},
{1, 0},
{-1, 0},
{0, -1}
};
private void bfs(int[][] grid, int x, int y){
Queue<Integer> que = new LinkedList<>();
que.offer(x);
que.offer(y);
count++;
grid[x][y] = 0;
while(!que.isEmpty()){
int currX = que.poll();
int currY = que.poll();
for(int i = 0; i < 4; i++){
int nextX = currX + dir[i][0];
int nextY = currY + dir[i][1];
if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length)
continue;
if(grid[nextX][nextY] == 1){
que.offer(nextX);
que.offer(nextY);
count++;
grid[nextX][nextY] = 0;
}
}
}
}
public int numEnclaves(int[][] grid) {
for(int i = 0; i < grid.length; i++){
if(grid[i][0] == 1)
bfs(grid, i, 0);
if(grid[i][grid[0].length - 1] == 1)
bfs(grid, i, grid[0].length - 1);
}
for(int j = 1; j < grid[0].length; j++){
if(grid[0][j] == 1)
bfs(grid, 0 , j);
if(grid[grid.length - 1][j] == 1)
bfs(grid, grid.length - 1, j);
}
count = 0;
for(int i = 1; i < grid.length - 1; i++){
for(int j = 1; j < grid[0].length - 1; j++){
if(grid[i][j] == 1)
bfs(grid,i ,j);
}
}
return count;
}
}
```
### Python ### Python
深度优先遍历 深度优先遍历

View File

@ -238,6 +238,21 @@ class Solution:
return total_sum - dp[target] - dp[target] return total_sum - dp[target] - dp[target]
```
卡哥版(简化版)
```python
class Solution:
def lastStoneWeightII(self, stones):
total_sum = sum(stones)
target = total_sum // 2
dp = [0] * (target + 1)
for stone in stones:
for j in range(target, stone - 1, -1):
dp[j] = max(dp[j], dp[j - stone] + stone)
return total_sum - 2* dp[-1]
``` ```
二维DP版 二维DP版
```python ```python

View File

@ -144,6 +144,11 @@ Java
*/ */
class Solution { class Solution {
public int longestCommonSubsequence(String text1, String text2) { public int longestCommonSubsequence(String text1, String text2) {
// char[] char1 = text1.toCharArray();
// char[] char2 = text2.toCharArray();
// 可以在一開始的時候就先把text1, text2 轉成char[],之後就不需要有這麼多爲了處理字串的調整
// 就可以和卡哥的code更一致
int[][] dp = new int[text1.length() + 1][text2.length() + 1]; // 先对dp数组做初始化操作 int[][] dp = new int[text1.length() + 1][text2.length() + 1]; // 先对dp数组做初始化操作
for (int i = 1 ; i <= text1.length() ; i++) { for (int i = 1 ; i <= text1.length() ; i++) {
char char1 = text1.charAt(i - 1); char char1 = text1.charAt(i - 1);
@ -203,21 +208,49 @@ class Solution {
``` ```
Python Python
2维DP
```python ```python
class Solution: class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int: def longestCommonSubsequence(self, text1: str, text2: str) -> int:
len1, len2 = len(text1)+1, len(text2)+1 # 创建一个二维数组 dp用于存储最长公共子序列的长度
dp = [[0 for _ in range(len1)] for _ in range(len2)] # 先对dp数组做初始化操作 dp = [[0] * (len(text2) + 1) for _ in range(len(text1) + 1)]
for i in range(1, len2):
for j in range(1, len1): # 开始列出状态转移方程
if text1[j-1] == text2[i-1]:
dp[i][j] = dp[i-1][j-1]+1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[-1][-1]
```
# 遍历 text1 和 text2填充 dp 数组
for i in range(1, len(text1) + 1):
for j in range(1, len(text2) + 1):
if text1[i - 1] == text2[j - 1]:
# 如果 text1[i-1] 和 text2[j-1] 相等,则当前位置的最长公共子序列长度为左上角位置的值加一
dp[i][j] = dp[i - 1][j - 1] + 1
else:
# 如果 text1[i-1] 和 text2[j-1] 不相等,则当前位置的最长公共子序列长度为上方或左方的较大值
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
# 返回最长公共子序列的长度
return dp[len(text1)][len(text2)]
```
1维DP
```python
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
dp = [0] * (n + 1) # 初始化一维DP数组
for i in range(1, m + 1):
prev = 0 # 保存上一个位置的最长公共子序列长度
for j in range(1, n + 1):
curr = dp[j] # 保存当前位置的最长公共子序列长度
if text1[i - 1] == text2[j - 1]:
# 如果当前字符相等,则最长公共子序列长度加一
dp[j] = prev + 1
else:
# 如果当前字符不相等,则选择保留前一个位置的最长公共子序列长度中的较大值
dp[j] = max(dp[j], dp[j - 1])
prev = curr # 更新上一个位置的最长公共子序列长度
return dp[n] # 返回最后一个位置的最长公共子序列长度作为结果
```
Go Go
```Go ```Go