mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 00:43:04 +08:00
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
This commit is contained in:
@ -409,7 +409,7 @@ impl Solution {
|
||||
// 递归
|
||||
impl Solution {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ class Solution {
|
||||
while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换
|
||||
left--;
|
||||
}
|
||||
// 向左滑动,找右边界
|
||||
// 向右滑动,找右边界
|
||||
while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。
|
||||
right++;
|
||||
}
|
||||
|
@ -98,8 +98,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// 时间复杂度: 最差情况所有元素都是唯一的。复杂度和全排列1都是 O(n! * n) 对于 n 个元素一共有 n! 中排列方案。而对于每一个答案,我们需要 O(n) 去复制最终放到 result 数组
|
||||
// 空间复杂度: O(n) 回溯树的深度取决于我们有多少个元素
|
||||
```
|
||||
* 时间复杂度: O(n)
|
||||
* 时间复杂度: O(n! * n)
|
||||
* 空间复杂度: O(n)
|
||||
|
||||
## 拓展
|
||||
|
@ -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
|
||||
|
@ -133,12 +133,13 @@ Java:
|
||||
class Solution {
|
||||
public int climbStairs(int n) {
|
||||
int[] dp = new int[n + 1];
|
||||
int m = 2;
|
||||
int m = 2; //有兩個物品:itme1重量爲一,item2重量爲二
|
||||
dp[0] = 1;
|
||||
|
||||
for (int i = 1; i <= n; i++) { // 遍历背包
|
||||
for (int j = 1; j <= m; j++) { //遍历物品
|
||||
if (i >= j) dp[i] += dp[i - j];
|
||||
if (i >= j) //當前的背包容量 大於 物品重量的時候,我們才需要記錄當前的這個裝得方法(方法數+)
|
||||
dp[i] += dp[i - j];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ exection -> execution (插入 'u')
|
||||
* 0 <= word1.length, word2.length <= 500
|
||||
* word1 和 word2 由小写英文字母组成
|
||||
|
||||
# 算法公开课
|
||||
**《代码随想录》算法视频公开课:[动态规划终极绝杀! LeetCode:72.编辑距离](https://www.bilibili.com/video/BV1we4y157wB/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
|
||||
|
||||
## 思路
|
||||
|
||||
|
@ -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
|
||||
class Solution {
|
||||
@ -271,6 +290,10 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
```Java
|
||||
|
||||
```
|
||||
|
||||
|
||||
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">
|
||||
|
@ -322,13 +322,10 @@ function maxProfit(prices: number[]): number {
|
||||
|
||||
```Rust
|
||||
impl Solution {
|
||||
fn max(a: i32, b: i32) -> i32 {
|
||||
if a > b { a } else { b }
|
||||
}
|
||||
pub fn max_profit(prices: Vec<i32>) -> i32 {
|
||||
let mut result = 0;
|
||||
for i in 1..prices.len() {
|
||||
result += Self::max(prices[i] - prices[i - 1], 0);
|
||||
result += (prices[i] - prices[i - 1]).max(0);
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -339,18 +336,14 @@ impl Solution {
|
||||
|
||||
```Rust
|
||||
impl Solution {
|
||||
fn max(a: i32, b: i32) -> i32 {
|
||||
if a > b { a } else { b }
|
||||
}
|
||||
pub fn max_profit(prices: Vec<i32>) -> i32 {
|
||||
let n = prices.len();
|
||||
let mut dp = vec![vec![0; 2]; n];
|
||||
dp[0][0] -= prices[0];
|
||||
for i in 1..n {
|
||||
dp[i][0] = Self::max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
|
||||
dp[i][1] = Self::max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
|
||||
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]);
|
||||
}
|
||||
Self::max(dp[n - 1][0], dp[n - 1][1])
|
||||
dp[prices.len() - 1][1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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
|
||||
// 优化空间
|
||||
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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -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
|
||||
// 深度优先遍历
|
||||
// 使用 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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -118,7 +118,7 @@ for (int i = startIndex; i < s.size(); i++) {
|
||||
continue;
|
||||
}
|
||||
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经填在的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经添加的子串
|
||||
}
|
||||
```
|
||||
|
||||
@ -189,7 +189,7 @@ private:
|
||||
continue;
|
||||
}
|
||||
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经填在的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经添加的子串
|
||||
}
|
||||
}
|
||||
bool isPalindrome(const string& s, int start, int end) {
|
||||
@ -245,7 +245,7 @@ private:
|
||||
continue;
|
||||
}
|
||||
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经填在的子串
|
||||
path.pop_back(); // 回溯过程,弹出本次已经添加的子串
|
||||
}
|
||||
}
|
||||
void computePalindrome(const string& s) {
|
||||
@ -437,7 +437,7 @@ class Solution:
|
||||
substring = s[startIndex:i + 1]
|
||||
path.append(substring)
|
||||
self.backtracking(s, i + 1, path, result, isPalindrome) # 寻找i+1为起始位置的子串
|
||||
path.pop() # 回溯过程,弹出本次已经填在的子串
|
||||
path.pop() # 回溯过程,弹出本次已经添加的子串
|
||||
|
||||
def computePalindrome(self, s, isPalindrome):
|
||||
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) { // 是回文子串
|
||||
path = append(path, str)
|
||||
dfs(s, i+1) // 寻找i+1为起始位置的子串
|
||||
path = path[:len(path)-1] // 回溯过程,弹出本次已经填在的子串
|
||||
path = path[:len(path)-1] // 回溯过程,弹出本次已经添加的子串
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
|
@ -467,9 +467,57 @@ class Solution:
|
||||
return " ".join(words)
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -227,7 +227,7 @@ class Solution {
|
||||
}
|
||||
}
|
||||
|
||||
//版本三:一维 dp数组
|
||||
//版本三:一维 dp数组 (下面有和卡哥邏輯一致的一維數組JAVA解法)
|
||||
class Solution {
|
||||
public int maxProfit(int k, int[] prices) {
|
||||
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:
|
||||
|
||||
|
@ -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">
|
||||
|
@ -176,6 +176,48 @@ public void dfs(char[][] grid, int i, int j){
|
||||
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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
|
@ -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">
|
||||
|
@ -163,6 +163,7 @@ class Solution(object):
|
||||
a_count = Counter(s)
|
||||
b_count = Counter(t)
|
||||
return a_count == b_count
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -390,6 +390,8 @@ public:
|
||||
|
||||
```Java
|
||||
//解法一
|
||||
|
||||
//方式一
|
||||
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
|
||||
// 解法2
|
||||
// 解法二
|
||||
class Solution {
|
||||
/**
|
||||
* 迭代法
|
||||
|
@ -177,15 +177,19 @@ class Solution {
|
||||
for (int j = 0; j <= n; j++) {
|
||||
dp[j] = max;
|
||||
}
|
||||
//如果不想要寫for-loop填充數組的話,也可以用JAVA內建的Arrays.fill()函數。
|
||||
//Arrays.fill(dp, Integer.MAX_VALUE);
|
||||
|
||||
//当和为0时,组合的个数为0
|
||||
dp[0] = 0;
|
||||
// 遍历物品
|
||||
for (int i = 1; i * i <= n; i++) {
|
||||
// 遍历背包
|
||||
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);
|
||||
}
|
||||
//}
|
||||
//不需要這個if statement,因爲在完全平方數這一題不會有"湊不成"的狀況發生( 一定可以用"1"來組成任何一個n),故comment掉這個if statement。
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
@ -356,6 +360,7 @@ var numSquares2 = function(n) {
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
// 先遍历物品
|
||||
function numSquares(n: number): number {
|
||||
const goodsNum: number = Math.floor(Math.sqrt(n));
|
||||
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">
|
||||
|
@ -148,6 +148,8 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
DP
|
||||
```python
|
||||
class Solution:
|
||||
def lengthOfLIS(self, nums: List[int]) -> int:
|
||||
@ -162,7 +164,31 @@ class Solution:
|
||||
result = max(result, dp[i]) #取长的子序列
|
||||
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
|
||||
// 动态规划求解
|
||||
|
@ -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
|
||||
// 一维数组优化
|
||||
class Solution {
|
||||
@ -248,23 +274,51 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
版本一
|
||||
```python
|
||||
from typing import List
|
||||
|
||||
class Solution:
|
||||
def maxProfit(self, prices: List[int]) -> int:
|
||||
n = len(prices)
|
||||
if n == 0:
|
||||
return 0
|
||||
dp = [[0] * 4 for _ in range(n)]
|
||||
dp[0][0] = -prices[0] #持股票
|
||||
dp = [[0] * 4 for _ in range(n)] # 创建动态规划数组,4个状态分别表示持有股票、不持有股票且处于冷冻期、不持有股票且不处于冷冻期、不持有股票且当天卖出后处于冷冻期
|
||||
dp[0][0] = -prices[0] # 初始状态:第一天持有股票的最大利润为买入股票的价格
|
||||
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][1] = max(dp[i-1][1], dp[i-1][3])
|
||||
dp[i][2] = dp[i-1][0] + prices[i]
|
||||
dp[i][3] = dp[i-1][2]
|
||||
return max(dp[n-1][3], dp[n-1][1], dp[n-1][2])
|
||||
```
|
||||
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][2] = dp[i-1][0] + prices[i] # 当前不持有股票且不处于冷冻期的最大利润等于前一天不持有股票的最大利润或者前一天处于冷冻期的最大利润
|
||||
dp[i][3] = dp[i-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
|
||||
// 最佳买卖股票时机含冷冻期 动态规划
|
||||
|
@ -355,23 +355,52 @@ func min(a, b int) int {
|
||||
Rust:
|
||||
|
||||
```rust
|
||||
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 0..coins.len() {
|
||||
for j in coins[i] as usize..=amount {
|
||||
if dp[j - coins[i] as usize] != i32::MAX {
|
||||
dp[j] = dp[j].min(dp[j - coins[i] as usize] + 1);
|
||||
// 遍历物品
|
||||
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 coin in coins {
|
||||
for i in coin as usize..=amount {
|
||||
if 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]
|
||||
}
|
||||
if dp[amount] == i32::MAX { -1 } else { 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
|
||||
// 遍历物品
|
||||
const coinChange = (coins, amount) => {
|
||||
if(!amount) {
|
||||
return 0;
|
||||
@ -380,7 +409,7 @@ const coinChange = (coins, amount) => {
|
||||
let dp = Array(amount + 1).fill(Infinity);
|
||||
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++) {
|
||||
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
|
||||
// 遍历物品
|
||||
function coinChange(coins: number[], amount: number): number {
|
||||
const dp: number[] = new Array(amount + 1).fill(Infinity);
|
||||
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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
|
@ -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">
|
||||
|
@ -262,10 +262,11 @@ class Solution:
|
||||
|
||||
# 计算切割点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] # 返回最终的计算结果
|
||||
|
||||
|
||||
```
|
||||
动态规划(版本二)
|
||||
```python
|
||||
|
@ -146,6 +146,7 @@ class Solution {
|
||||
|
||||
```
|
||||
|
||||
Python:
|
||||
(版本一)使用数组
|
||||
```python
|
||||
class Solution:
|
||||
@ -210,7 +211,6 @@ class Solution:
|
||||
class Solution:
|
||||
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
|
||||
return all(ransomNote.count(c) <= magazine.count(c) for c in set(ransomNote))
|
||||
|
||||
```
|
||||
|
||||
Go:
|
||||
|
@ -383,6 +383,21 @@ class Solution:
|
||||
return True
|
||||
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版
|
||||
```python
|
||||
|
@ -290,26 +290,21 @@ int findMinArrowShots(int** points, int pointsSize, int* pointsColSize){
|
||||
|
||||
### Rust
|
||||
```Rust
|
||||
use std::cmp;
|
||||
impl Solution {
|
||||
pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 {
|
||||
if points.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
points.sort_by_key(|point| point[0]);
|
||||
|
||||
let size = points.len();
|
||||
let mut count = 1;
|
||||
|
||||
for i in 1..size {
|
||||
if points[i][0] > points[i-1][1] {
|
||||
count += 1;
|
||||
} else {
|
||||
points[i][1] = cmp::min(points[i][1], points[i-1][1]);
|
||||
let mut result = 1;
|
||||
for i in 1..points.len() {
|
||||
if points[i][0] > points[i - 1][1] {
|
||||
result += 1;
|
||||
} else {
|
||||
points[i][1] = points[i][1].min(points[i - 1][1])
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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时的情况),有唯一一种取法可取到j,dp[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
|
||||
|
@ -14,10 +14,15 @@
|
||||
|
||||
* 输入: "sea", "eat"
|
||||
* 输出: 2
|
||||
* 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
|
||||
* 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
|
||||
|
||||
|
||||
# 算法公开课
|
||||
|
||||
**《代码随想录》算法视频公开课:[动态规划之子序列,还是为了编辑距离做铺垫 | LeetCode:583.两个字符串的删除操(https://www.bilibili.com/video/BV1we4y157wB/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
|
||||
|
||||
|
||||
## 思路
|
||||
|
||||
### 动态规划一
|
||||
|
||||
本题和[动态规划: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:
|
||||
|
@ -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:
|
||||
|
||||
|
@ -177,6 +177,7 @@ Java:
|
||||
dp[i] = 1;
|
||||
}
|
||||
int res = 1;
|
||||
//可以注意到,這邊的 i 是從 0 開始,所以會出現和卡哥的C++ code有差異的地方,在一些地方會看到有 i + 1 的偏移。
|
||||
for (int i = 0; i < nums.length - 1; i++) {
|
||||
if (nums[i + 1] > nums[i]) {
|
||||
dp[i + 1] = dp[i] + 1;
|
||||
@ -208,7 +209,7 @@ public static int findLengthOfLCIS(int[] nums) {
|
||||
|
||||
Python:
|
||||
|
||||
> 动态规划:
|
||||
DP
|
||||
```python
|
||||
class Solution:
|
||||
def findLengthOfLCIS(self, nums: List[int]) -> int:
|
||||
@ -223,8 +224,27 @@ class Solution:
|
||||
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
|
||||
class Solution:
|
||||
def findLengthOfLCIS(self, nums: List[int]) -> int:
|
||||
|
@ -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){
|
||||
//搜索边界:i,j超过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
|
||||
### BFS
|
||||
@ -261,39 +390,6 @@ class Solution:
|
||||
if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]):
|
||||
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){
|
||||
//搜索边界:i,j超过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">
|
||||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||||
|
@ -667,100 +667,128 @@ Go:
|
||||
//单链表实现
|
||||
package main
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 单链表写法 //
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type SingleNode struct {
|
||||
Val int
|
||||
Next *SingleNode
|
||||
Val int // 节点的值
|
||||
Next *SingleNode // 下一个节点的指针
|
||||
}
|
||||
|
||||
type SingleLinkedList struct {
|
||||
dummyHead *SingleNode
|
||||
Size int
|
||||
type MyLinkedList struct {
|
||||
dummyHead *SingleNode // 虚拟头节点
|
||||
Size int // 链表大小
|
||||
}
|
||||
|
||||
// 初始化链表
|
||||
func (list *SingleLinkedList) Init() *SingleLinkedList {
|
||||
list.Size = 0
|
||||
list.dummyHead = new(SingleNode)
|
||||
return list
|
||||
func main() {
|
||||
list := Constructor() // 初始化链表
|
||||
list.AddAtHead(100) // 在头部添加元素
|
||||
list.AddAtTail(242) // 在尾部添加元素
|
||||
list.AddAtTail(777) // 在尾部添加元素
|
||||
list.AddAtIndex(1, 99999) // 在指定位置添加元素
|
||||
list.printLinkedList() // 打印链表
|
||||
}
|
||||
|
||||
// 获取第index个节点数值
|
||||
func (list *SingleLinkedList) get(index int) int {
|
||||
if list != nil || index < 0 || index > list.Size {
|
||||
/** Initialize your data structure here. */
|
||||
func Constructor() MyLinkedList {
|
||||
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
|
||||
}
|
||||
// 让cur等于真正头节点
|
||||
cur := list.dummyHead.Next
|
||||
for i := 0; i < index; i++ {
|
||||
cur := this.dummyHead.Next // 设置当前节点为真实头节点
|
||||
for i := 0; i < index; i++ { // 遍历到索引所在的节点
|
||||
cur = cur.Next
|
||||
}
|
||||
return cur.Val
|
||||
return cur.Val // 返回节点值
|
||||
}
|
||||
|
||||
// 在链表最前面插入一个节点
|
||||
func (list *SingleLinkedList) addAtHead(val int) {
|
||||
/** Add a node of value val before the first element of the linked list. After
|
||||
the insertion, the new node will be the first node of the linked list. */
|
||||
func (this *MyLinkedList) AddAtHead(val int) {
|
||||
// 以下两行代码可用一行代替
|
||||
// newNode := new(SingleNode)
|
||||
// newNode.Val = val
|
||||
newNode := &SingleNode{Val: val}
|
||||
|
||||
newNode.Next = list.dummyHead.Next
|
||||
list.dummyHead.Next = newNode
|
||||
list.Size++
|
||||
newNode := &SingleNode{Val: val} // 创建新节点
|
||||
newNode.Next = this.dummyHead.Next // 新节点指向当前头节点
|
||||
this.dummyHead.Next = newNode // 新节点变为头节点
|
||||
this.Size++ // 链表大小增加1
|
||||
}
|
||||
|
||||
// 在链表最后面插入一个节点
|
||||
func (list *SingleLinkedList) addAtTail(val int) {
|
||||
newNode := &SingleNode{Val: val}
|
||||
cur := list.dummyHead
|
||||
for cur.Next != nil {
|
||||
/** Append a node of value val to the last element of the linked list. */
|
||||
func (this *MyLinkedList) AddAtTail(val int) {
|
||||
newNode := &SingleNode{Val: val} // 创建新节点
|
||||
cur := this.dummyHead // 设置当前节点为虚拟头节点
|
||||
for cur.Next != nil { // 遍历到最后一个节点
|
||||
cur = cur.Next
|
||||
}
|
||||
cur.Next = newNode
|
||||
list.Size++
|
||||
cur.Next = newNode // 在尾部添加新节点
|
||||
this.Size++ // 链表大小增加1
|
||||
}
|
||||
|
||||
// 打印链表
|
||||
func (list *SingleLinkedList) printLinkedList() {
|
||||
cur := list.dummyHead
|
||||
for cur.Next != nil {
|
||||
fmt.Println(cur.Next.Val)
|
||||
cur = cur.Next
|
||||
}
|
||||
}
|
||||
|
||||
// 在第index个节点之前插入新节点
|
||||
func (list *SingleLinkedList) addAtIndex(index int, val int) {
|
||||
if index < 0 {
|
||||
/** Add a node of value val before the index-th node in the linked list. If
|
||||
index equals to the length of linked list, the node will be appended to the
|
||||
end of linked list. If index is greater than the length, the node will not be
|
||||
inserted. */
|
||||
func (this *MyLinkedList) AddAtIndex(index int, val int) {
|
||||
if index < 0 { // 如果索引小于0,设置为0
|
||||
index = 0
|
||||
} else if index > list.Size {
|
||||
} else if index > this.Size { // 如果索引大于链表长度,直接返回
|
||||
return
|
||||
}
|
||||
|
||||
newNode := &SingleNode{Val: val}
|
||||
cur := list.dummyHead //用虚拟头节点不用考虑在头部插入的情况
|
||||
for i := 0; i < index; i++ {
|
||||
newNode := &SingleNode{Val: val} // 创建新节点
|
||||
cur := this.dummyHead // 设置当前节点为虚拟头节点
|
||||
for i := 0; i < index; i++ { // 遍历到指定索引的前一个节点
|
||||
cur = cur.Next
|
||||
}
|
||||
newNode.Next = cur.Next
|
||||
cur.Next = newNode
|
||||
list.Size++
|
||||
newNode.Next = cur.Next // 新节点指向原索引节点
|
||||
cur.Next = newNode // 原索引的前一个节点指向新节点
|
||||
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
|
||||
|
@ -154,6 +154,25 @@ class Solution {
|
||||
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:
|
||||
|
@ -302,37 +302,99 @@ class Solution {
|
||||
|
||||
Python:
|
||||
|
||||
> 动态规划:
|
||||
2维DP
|
||||
```python
|
||||
class Solution:
|
||||
def findLength(self, A: List[int], B: List[int]) -> int:
|
||||
dp = [[0] * (len(B)+1) for _ in range(len(A)+1)]
|
||||
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(1, len(A)+1):
|
||||
for j in range(1, len(B)+1):
|
||||
if A[i-1] == B[j-1]:
|
||||
dp[i][j] = dp[i-1][j-1] + 1
|
||||
result = max(result, dp[i][j])
|
||||
|
||||
# 遍历数组 nums1
|
||||
for i in range(1, len(nums1) + 1):
|
||||
# 遍历数组 nums2
|
||||
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
|
||||
|
||||
```
|
||||
|
||||
> 动态规划:滚动数组
|
||||
1维DP
|
||||
```python
|
||||
class Solution:
|
||||
def findLength(self, A: List[int], B: List[int]) -> int:
|
||||
dp = [0] * (len(B) + 1)
|
||||
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
|
||||
# 创建一个一维数组 dp,用于存储最长公共子数组的长度
|
||||
dp = [0] * (len(nums2) + 1)
|
||||
# 记录最长公共子数组的长度
|
||||
result = 0
|
||||
for i in range(1, len(A)+1):
|
||||
for j in range(len(B), 0, -1):
|
||||
if A[i-1] == B[j-1]:
|
||||
dp[j] = dp[j-1] + 1
|
||||
|
||||
# 遍历数组 nums1
|
||||
for i in range(1, len(nums1) + 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:
|
||||
dp[j] = 0 #注意这里不相等的时候要有赋0的操作
|
||||
result = max(result, dp[j])
|
||||
# 如果不相等,将当前位置的值置为零
|
||||
dp[j] = 0
|
||||
# 更新 prev 变量为当前位置的值,供下一次迭代使用
|
||||
prev = current
|
||||
|
||||
# 返回最长公共子数组的长度
|
||||
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
|
||||
func findLength(A []int, B []int) int {
|
||||
|
@ -282,7 +282,35 @@ class Solution:
|
||||
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
|
||||
func minCostClimbingStairs(cost []int) int {
|
||||
|
@ -195,9 +195,9 @@ var largestSumAfterKNegations = function(nums, k) {
|
||||
nums[nums.length-1] = - nums[nums.length-1]
|
||||
k--;
|
||||
}
|
||||
return nums.reduce((a, b) => {
|
||||
a + b
|
||||
})
|
||||
|
||||
// 使用箭头函数的隐式返回值时,需使用简写省略花括号,否则要在 a + b 前加上 return
|
||||
return nums.reduce((a, b) => a + b)
|
||||
};
|
||||
|
||||
// 版本二 (优化: 一次遍历)
|
||||
|
@ -149,7 +149,63 @@ public:
|
||||
|
||||
### 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
|
||||
class Solution {
|
||||
@ -206,7 +262,7 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
广度优先遍历版本:
|
||||
广度优先遍历(使用visited數組)
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
深度优先遍历
|
||||
|
@ -238,6 +238,21 @@ class Solution:
|
||||
|
||||
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版
|
||||
```python
|
||||
|
@ -144,6 +144,11 @@ Java:
|
||||
*/
|
||||
class Solution {
|
||||
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数组做初始化操作
|
||||
for (int i = 1 ; i <= text1.length() ; i++) {
|
||||
char char1 = text1.charAt(i - 1);
|
||||
@ -203,21 +208,49 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
2维DP
|
||||
```python
|
||||
class Solution:
|
||||
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
|
||||
len1, len2 = len(text1)+1, len(text2)+1
|
||||
dp = [[0 for _ in range(len1)] for _ in range(len2)] # 先对dp数组做初始化操作
|
||||
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
|
||||
# 创建一个二维数组 dp,用于存储最长公共子序列的长度
|
||||
dp = [[0] * (len(text2) + 1) for _ in range(len(text1) + 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:
|
||||
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
|
||||
return dp[-1][-1]
|
||||
```
|
||||
# 如果 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
|
||||
|
Reference in New Issue
Block a user