mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-06 23:28:29 +08:00
Merge branch 'master' of github.com:youngyangyang04/leetcode-master
This commit is contained in:
@ -409,6 +409,66 @@ var letterCombinations = function(digits) {
|
||||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
char* path;
|
||||
int pathTop;
|
||||
char** result;
|
||||
int resultTop;
|
||||
char* letterMap[10] = {"", //0
|
||||
"", //1
|
||||
"abc", //2
|
||||
"def", //3
|
||||
"ghi", //4
|
||||
"jkl", //5
|
||||
"mno", //6
|
||||
"pqrs", //7
|
||||
"tuv", //8
|
||||
"wxyz", //9
|
||||
};
|
||||
void backTracking(char* digits, int index) {
|
||||
//若当前下标等于digits数组长度
|
||||
if(index == strlen(digits)) {
|
||||
//复制digits数组,因为最后要多存储一个0,所以数组长度要+1
|
||||
char* tempString = (char*)malloc(sizeof(char) * strlen(digits) + 1);
|
||||
int j;
|
||||
for(j = 0; j < strlen(digits); j++) {
|
||||
tempString[j] = path[j];
|
||||
}
|
||||
//char数组最后要以0结尾
|
||||
tempString[strlen(digits)] = 0;
|
||||
result[resultTop++] = tempString;
|
||||
return ;
|
||||
}
|
||||
//将字符数字转换为真的数字
|
||||
int digit = digits[index] - '0';
|
||||
//找到letterMap中对应的字符串
|
||||
char* letters = letterMap[digit];
|
||||
int i;
|
||||
for(i = 0; i < strlen(letters); i++) {
|
||||
path[pathTop++] = letters[i];
|
||||
//递归,处理下一层数字
|
||||
backTracking(digits, index+1);
|
||||
pathTop--;
|
||||
}
|
||||
}
|
||||
|
||||
char ** letterCombinations(char * digits, int* returnSize){
|
||||
//初始化path和result
|
||||
path = (char*)malloc(sizeof(char) * strlen(digits));
|
||||
result = (char**)malloc(sizeof(char*) * 300);
|
||||
|
||||
*returnSize = 0;
|
||||
//若digits数组中元素个数为0,返回空集
|
||||
if(strlen(digits) == 0)
|
||||
return result;
|
||||
pathTop = resultTop = 0;
|
||||
backTracking(digits, 0);
|
||||
*returnSize = resultTop;
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -376,6 +376,61 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func solveSudoku(board [][]byte) {
|
||||
var backtracking func(board [][]byte) bool
|
||||
backtracking=func(board [][]byte) bool{
|
||||
for i:=0;i<9;i++{
|
||||
for j:=0;j<9;j++{
|
||||
//判断此位置是否适合填数字
|
||||
if board[i][j]!='.'{
|
||||
continue
|
||||
}
|
||||
//尝试填1-9
|
||||
for k:='1';k<='9';k++{
|
||||
if isvalid(i,j,byte(k),board)==true{//如果满足要求就填
|
||||
board[i][j]=byte(k)
|
||||
if backtracking(board)==true{
|
||||
return true
|
||||
}
|
||||
board[i][j]='.'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
backtracking(board)
|
||||
}
|
||||
//判断填入数字是否满足要求
|
||||
func isvalid(row,col int,k byte,board [][]byte)bool{
|
||||
for i:=0;i<9;i++{//行
|
||||
if board[row][i]==k{
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i:=0;i<9;i++{//列
|
||||
if board[i][col]==k{
|
||||
return false
|
||||
}
|
||||
}
|
||||
//方格
|
||||
startrow:=(row/3)*3
|
||||
startcol:=(col/3)*3
|
||||
for i:=startrow;i<startrow+3;i++{
|
||||
for j:=startcol;j<startcol+3;j++{
|
||||
if board[i][j]==k{
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
var solveSudoku = function(board) {
|
||||
|
@ -346,6 +346,60 @@ var combinationSum = function(candidates, target) {
|
||||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
int** ans;
|
||||
int ansTop;
|
||||
//记录每一个和等于target的path数组长度
|
||||
int* length;
|
||||
|
||||
void backTracking(int target, int index, int* candidates, int candidatesSize, int sum) {
|
||||
//若sum>=target就应该终止遍历
|
||||
if(sum >= target) {
|
||||
//若sum等于target,将当前的组合放入ans数组中
|
||||
if(sum == target) {
|
||||
int* tempPath = (int*)malloc(sizeof(int) * pathTop);
|
||||
int j;
|
||||
for(j = 0; j < pathTop; j++) {
|
||||
tempPath[j] = path[j];
|
||||
}
|
||||
ans[ansTop] = tempPath;
|
||||
length[ansTop++] = pathTop;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i = index; i < candidatesSize; i++) {
|
||||
//将当前数字大小加入sum
|
||||
sum+=candidates[i];
|
||||
path[pathTop++] = candidates[i];
|
||||
backTracking(target, i, candidates, candidatesSize, sum);
|
||||
sum-=candidates[i];
|
||||
pathTop--;
|
||||
}
|
||||
}
|
||||
|
||||
int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
|
||||
//初始化变量
|
||||
path = (int*)malloc(sizeof(int) * 50);
|
||||
ans = (int**)malloc(sizeof(int*) * 200);
|
||||
length = (int*)malloc(sizeof(int) * 200);
|
||||
ansTop = pathTop = 0;
|
||||
backTracking(target, 0, candidates, candidatesSize, 0);
|
||||
|
||||
//设置返回的数组大小
|
||||
*returnSize = ansTop;
|
||||
*returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
|
||||
int i;
|
||||
for(i = 0; i < ansTop; i++) {
|
||||
(*returnColumnSizes)[i] = length[i];
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -392,7 +392,66 @@ var combinationSum2 = function(candidates, target) {
|
||||
}
|
||||
};
|
||||
```
|
||||
C:
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
int** ans;
|
||||
int ansTop;
|
||||
//记录ans中每一个一维数组的大小
|
||||
int* length;
|
||||
int cmp(const void* a1, const void* a2) {
|
||||
return *((int*)a1) - *((int*)a2);
|
||||
}
|
||||
|
||||
void backTracking(int* candidates, int candidatesSize, int target, int sum, int startIndex) {
|
||||
if(sum >= target) {
|
||||
//若sum等于target,复制当前path进入
|
||||
if(sum == target) {
|
||||
int* tempPath = (int*)malloc(sizeof(int) * pathTop);
|
||||
int j;
|
||||
for(j = 0; j < pathTop; j++) {
|
||||
tempPath[j] = path[j];
|
||||
}
|
||||
length[ansTop] = pathTop;
|
||||
ans[ansTop++] = tempPath;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i = startIndex; i < candidatesSize; i++) {
|
||||
//对同一层树中使用过的元素跳过
|
||||
if(i > startIndex && candidates[i] == candidates[i-1])
|
||||
continue;
|
||||
path[pathTop++] = candidates[i];
|
||||
sum += candidates[i];
|
||||
backTracking(candidates, candidatesSize, target, sum, i + 1);
|
||||
//回溯
|
||||
sum -= candidates[i];
|
||||
pathTop--;
|
||||
}
|
||||
}
|
||||
|
||||
int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
|
||||
path = (int*)malloc(sizeof(int) * 50);
|
||||
ans = (int**)malloc(sizeof(int*) * 100);
|
||||
length = (int*)malloc(sizeof(int) * 100);
|
||||
pathTop = ansTop = 0;
|
||||
//快速排序candidates,让相同元素挨到一起
|
||||
qsort(candidates, candidatesSize, sizeof(int), cmp);
|
||||
|
||||
backTracking(candidates, candidatesSize, target, 0, 0);
|
||||
|
||||
*returnSize = ansTop;
|
||||
*returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
|
||||
int i;
|
||||
for(i = 0; i < ansTop; i++) {
|
||||
(*returnColumnSizes)[i] = length[i];
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -134,6 +134,43 @@ public:
|
||||
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。
|
||||
空间复杂度为O(1)。
|
||||
|
||||
|
||||
|
||||
一种更简便的双指针方法:
|
||||
|
||||
之前的双指针方法的原理是固定“底”的位置,往两边找比它高的“壁”,循环若干次求和。
|
||||
|
||||
我们逆向思维,把“壁”用两个初始位置在数组首末位置的指针表示,“壁”往中间推,同样可以让每个“底”都能找到最高的“壁”
|
||||
|
||||
本质上就是改变了运算方向,从而减少了重复运算
|
||||
|
||||
代码如下:
|
||||
|
||||
```C
|
||||
int trap(int* height, int heightSize) {
|
||||
int ans = 0;
|
||||
int left = 0, right = heightSize - 1; //初始化两个指针到左右两边
|
||||
int leftMax = 0, rightMax = 0; //这两个值用来记录左右的“壁”的最高值
|
||||
while (left < right) { //两个指针重合就结束
|
||||
leftMax = fmax(leftMax, height[left]);
|
||||
rightMax = fmax(rightMax, height[right]);
|
||||
if (leftMax < rightMax) {
|
||||
ans += leftMax - height[left]; //这里考虑的是下标为left的“底”能装多少水
|
||||
++left;//指针的移动次序是这个方法的关键
|
||||
//这里左指针右移是因为左“墙”较矮,左边这一片实际情况下的盛水量是受制于这个矮的左“墙”的
|
||||
//而较高的右边在实际情况下的限制条件可能不是当前的左“墙”,比如限制条件可能是右“墙”,就能装更高的水,
|
||||
}
|
||||
else {
|
||||
ans += rightMax - height[right]; //同理,考虑下标为right的元素
|
||||
--right;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
```
|
||||
时间复杂度 O(n)
|
||||
空间复杂度 O(1)
|
||||
|
||||
## 动态规划解法
|
||||
|
||||
在上一节的双指针解法中,我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。
|
||||
|
@ -469,6 +469,65 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
|
||||
//初始化返回的结果数组的大小
|
||||
*returnSize = n;
|
||||
*returnColumnSizes = (int*)malloc(sizeof(int) * n);
|
||||
//初始化返回结果数组ans
|
||||
int** ans = (int**)malloc(sizeof(int*) * n);
|
||||
int i;
|
||||
for(i = 0; i < n; i++) {
|
||||
ans[i] = (int*)malloc(sizeof(int) * n);
|
||||
(*returnColumnSizes)[i] = n;
|
||||
}
|
||||
|
||||
//设置每次循环的起始位置
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
//设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
|
||||
int mid = n / 2;
|
||||
//循环圈数
|
||||
int loop = n / 2;
|
||||
//偏移数
|
||||
int offset = 1;
|
||||
//当前要添加的元素
|
||||
int count = 1;
|
||||
|
||||
while(loop) {
|
||||
int i = startX;
|
||||
int j = startY;
|
||||
//模拟上侧从左到右
|
||||
for(; j < startY + n - offset; j++) {
|
||||
ans[startX][j] = count++;
|
||||
}
|
||||
//模拟右侧从上到下
|
||||
for(; i < startX + n - offset; i++) {
|
||||
ans[i][j] = count++;
|
||||
}
|
||||
//模拟下侧从右到左
|
||||
for(; j > startY; j--) {
|
||||
ans[i][j] = count++;
|
||||
}
|
||||
//模拟左侧从下到上
|
||||
for(; i > startX; i--) {
|
||||
ans[i][j] = count++;
|
||||
}
|
||||
//偏移值每次加2
|
||||
offset+=2;
|
||||
//遍历起始位置每次+1
|
||||
startX++;
|
||||
startY++;
|
||||
loop--;
|
||||
}
|
||||
//若n为奇数需要单独给矩阵中间赋值
|
||||
if(n%2)
|
||||
ans[mid][mid] = count;
|
||||
|
||||
return ans;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -1173,7 +1173,7 @@ public:
|
||||
if (root != NULL) que.push(root);
|
||||
while (!que.empty()) {
|
||||
int size = que.size();
|
||||
vector<int> vec;
|
||||
// vector<int> vec;
|
||||
Node* nodePre;
|
||||
Node* node;
|
||||
for (int i = 0; i < size; i++) {
|
||||
@ -1570,9 +1570,43 @@ class Solution:
|
||||
return len(result)
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func maxDepth(root *TreeNode) int {
|
||||
ans:=0
|
||||
if root==nil{
|
||||
return 0
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
}
|
||||
ans++//记录深度,其他的是层序遍历的板子
|
||||
}
|
||||
return ans
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
JavaScript:
|
||||
|
||||
# 111.二叉树的最小深度
|
||||
@ -1674,6 +1708,46 @@ class Solution:
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
/**
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func minDepth(root *TreeNode) int {
|
||||
ans:=0
|
||||
if root==nil{
|
||||
return 0
|
||||
}
|
||||
queue:=list.New()
|
||||
queue.PushBack(root)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()
|
||||
for i:=0;i<length;i++{
|
||||
node:=queue.Remove(queue.Front()).(*TreeNode)
|
||||
if node.Left==nil&&node.Right==nil{//当前节点没有左右节点,则代表此层是最小层
|
||||
return ans+1//返回当前层 ans代表是上一层
|
||||
}
|
||||
if node.Left!=nil{
|
||||
queue.PushBack(node.Left)
|
||||
}
|
||||
if node.Right!=nil{
|
||||
queue.PushBack(node.Right)
|
||||
}
|
||||
}
|
||||
ans++//记录层数
|
||||
|
||||
|
||||
}
|
||||
return ans+1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
JavaScript:
|
||||
|
||||
|
||||
|
@ -214,6 +214,26 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
```java
|
||||
// 解法1
|
||||
class Solution {
|
||||
public int maxProfit(int[] prices) {
|
||||
if (prices == null || prices.length == 0) return 0;
|
||||
int length = prices.length;
|
||||
// dp[i][0]代表第i天持有股票的最大收益
|
||||
// dp[i][1]代表第i天不持有股票的最大收益
|
||||
int[][] dp = new int[length][2];
|
||||
int result = 0;
|
||||
dp[0][0] = -prices[0];
|
||||
dp[0][1] = 0;
|
||||
for (int i = 1; i < length; i++) {
|
||||
dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
|
||||
dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
|
||||
}
|
||||
return dp[length - 1][1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
``` java
|
||||
class Solution { // 动态规划解法
|
||||
|
@ -202,6 +202,7 @@ Go:
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
// 方法一:动态规划(dp 数组)
|
||||
const maxProfit = (prices) => {
|
||||
let dp = Array.from(Array(prices.length), () => Array(2).fill(0));
|
||||
// dp[i][0] 表示第i天持有股票所得现金。
|
||||
@ -222,6 +223,21 @@ const maxProfit = (prices) => {
|
||||
|
||||
return dp[prices.length -1][0];
|
||||
};
|
||||
|
||||
// 方法二:动态规划(滚动数组)
|
||||
const maxProfit = (prices) => {
|
||||
// 滚动数组
|
||||
// have: 第i天持有股票最大收益; notHave: 第i天不持有股票最大收益
|
||||
let n = prices.length,
|
||||
have = -prices[0],
|
||||
notHave = 0;
|
||||
for (let i = 1; i < n; i++) {
|
||||
have = Math.max(have, notHave - prices[i]);
|
||||
notHave = Math.max(notHave, have + prices[i]);
|
||||
}
|
||||
// 最终手里不持有股票才能保证收益最大化
|
||||
return notHave;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -281,6 +281,48 @@ func canCompleteCircuit(gas []int, cost []int) int {
|
||||
```
|
||||
|
||||
Javascript:
|
||||
暴力:
|
||||
```js
|
||||
var canCompleteCircuit = function(gas, cost) {
|
||||
for(let i = 0; i < cost.length; i++) {
|
||||
let rest = gas[i] - cost[i] //记录剩余油量
|
||||
// 以i为起点行驶一圈,index为下一个目的地
|
||||
let index = (i + 1) % cost.length
|
||||
while(rest > 0 && index !== i) {
|
||||
rest += gas[index] - cost[index]
|
||||
index = (index + 1) % cost.length
|
||||
}
|
||||
if(rest >= 0 && index === i) return i
|
||||
}
|
||||
return -1
|
||||
};
|
||||
```
|
||||
解法一:
|
||||
```js
|
||||
var canCompleteCircuit = function(gas, cost) {
|
||||
let curSum = 0
|
||||
let min = Infinity
|
||||
for(let i = 0; i < gas.length; i++) {
|
||||
let rest = gas[i] - cost[i]
|
||||
curSum += rest
|
||||
if(curSum < min) {
|
||||
min = curSum
|
||||
}
|
||||
}
|
||||
if(curSum < 0) return -1 //1.总油量 小于 总消耗量
|
||||
if(min >= 0) return 0 //2. 说明油箱里油没断过
|
||||
//3. 从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点
|
||||
for(let i = gas.length -1; i >= 0; i--) {
|
||||
let rest = gas[i] - cost[i]
|
||||
min += rest
|
||||
if(min >= 0) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
解法二:
|
||||
```Javascript
|
||||
var canCompleteCircuit = function(gas, cost) {
|
||||
const gasLen = gas.length
|
||||
|
@ -392,7 +392,63 @@ var combinationSum3 = function(k, n) {
|
||||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
int** ans;
|
||||
int ansTop;
|
||||
int getPathSum() {
|
||||
int i;
|
||||
int sum = 0;
|
||||
for(i = 0; i < pathTop; i++) {
|
||||
sum += path[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void backtracking(int targetSum, int k, int sum, int startIndex) {
|
||||
if(pathTop == k) {
|
||||
if(sum == targetSum) {
|
||||
int* tempPath = (int*)malloc(sizeof(int) * k);
|
||||
int j;
|
||||
for(j = 0; j < k; j++)
|
||||
tempPath[j] = path[j];
|
||||
ans[ansTop++] = tempPath;
|
||||
}
|
||||
// 如果path.size() == k 但sum != targetSum 直接返回
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
//从startIndex开始遍历,一直遍历到9
|
||||
for (i = startIndex; i <= 9; i++) {
|
||||
sum += i; // 处理
|
||||
path[pathTop++] = i; // 处理
|
||||
backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
|
||||
sum -= i; // 回溯
|
||||
pathTop--;; // 回溯
|
||||
}
|
||||
}
|
||||
|
||||
int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes){
|
||||
//初始化辅助变量
|
||||
path = (int*)malloc(sizeof(int) * k);
|
||||
ans = (int**)malloc(sizeof(int*) * 20);
|
||||
pathTop = ansTop = 0;
|
||||
|
||||
backtracking(n, k, 0, 1);
|
||||
|
||||
//设置返回的二维数组中元素个数为ansTop
|
||||
*returnSize = ansTop;
|
||||
//设置二维数组中每个元素个数的大小为k
|
||||
*returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
|
||||
int i;
|
||||
for(i = 0; i < ansTop; i++) {
|
||||
(*returnColumnSizes)[i] = k;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -363,7 +363,9 @@ class Solution:
|
||||
return root
|
||||
```
|
||||
|
||||
### Go
|
||||
### Go
|
||||
|
||||
递归版本的前序遍历
|
||||
|
||||
```Go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
@ -381,6 +383,96 @@ func invertTree(root *TreeNode) *TreeNode {
|
||||
}
|
||||
```
|
||||
|
||||
递归版本的后序遍历
|
||||
|
||||
```go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
if root==nil{
|
||||
return root
|
||||
}
|
||||
invertTree(root.Left)//遍历左节点
|
||||
invertTree(root.Right)//遍历右节点
|
||||
root.Left,root.Right=root.Right,root.Left//交换
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
迭代版本的前序遍历
|
||||
|
||||
```go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
stack:=[]*TreeNode{}
|
||||
node:=root
|
||||
for node!=nil||len(stack)>0{
|
||||
for node!=nil{
|
||||
node.Left,node.Right=node.Right,node.Left//交换
|
||||
stack=append(stack,node)
|
||||
node=node.Left
|
||||
}
|
||||
node=stack[len(stack)-1]
|
||||
stack=stack[:len(stack)-1]
|
||||
node=node.Right
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
迭代版本的后序遍历
|
||||
|
||||
```go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
stack:=[]*TreeNode{}
|
||||
node:=root
|
||||
var prev *TreeNode
|
||||
for node!=nil||len(stack)>0{
|
||||
for node!=nil{
|
||||
stack=append(stack,node)
|
||||
node=node.Left
|
||||
}
|
||||
node=stack[len(stack)-1]
|
||||
stack=stack[:len(stack)-1]
|
||||
if node.Right==nil||node.Right==prev{
|
||||
node.Left,node.Right=node.Right,node.Left//交换
|
||||
prev=node
|
||||
node=nil
|
||||
}else {
|
||||
stack=append(stack,node)
|
||||
node=node.Right
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
层序遍历
|
||||
|
||||
```go
|
||||
func invertTree(root *TreeNode) *TreeNode {
|
||||
if root==nil{
|
||||
return root
|
||||
}
|
||||
queue:=list.New()
|
||||
node:=root
|
||||
queue.PushBack(node)
|
||||
for queue.Len()>0{
|
||||
length:=queue.Len()
|
||||
for i:=0;i<length;i++{
|
||||
e:=queue.Remove(queue.Front()).(*TreeNode)
|
||||
e.Left,e.Right=e.Right,e.Left//交换
|
||||
if e.Left!=nil{
|
||||
queue.PushBack(e.Left)
|
||||
}
|
||||
if e.Right!=nil{
|
||||
queue.PushBack(e.Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### JavaScript
|
||||
|
||||
使用递归版本的前序遍历
|
||||
|
@ -256,6 +256,25 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
pub fn is_anagram(s: String, t: String) -> bool {
|
||||
let mut record = vec![0; 26];
|
||||
|
||||
let baseChar = 'a';
|
||||
|
||||
for byte in s.bytes() {
|
||||
record[byte as usize - baseChar as usize] += 1;
|
||||
}
|
||||
for byte in t.bytes() {
|
||||
record[byte as usize - baseChar as usize] -= 1;
|
||||
}
|
||||
|
||||
record.iter().filter(|x| **x != 0).count() == 0
|
||||
}
|
||||
}
|
||||
```
|
||||
## 相关题目
|
||||
|
||||
* 383.赎金信
|
||||
|
@ -408,25 +408,57 @@ class Solution {
|
||||
Python:
|
||||
```Python
|
||||
class Solution:
|
||||
"""二叉树的所有路径 递归法"""
|
||||
|
||||
def binaryTreePaths(self, root: TreeNode) -> List[str]:
|
||||
path=[]
|
||||
res=[]
|
||||
def backtrace(root, path):
|
||||
if not root:return
|
||||
path.append(root.val)
|
||||
if (not root.left)and (not root.right):
|
||||
res.append(path[:])
|
||||
ways=[]
|
||||
if root.left:ways.append(root.left)
|
||||
if root.right:ways.append(root.right)
|
||||
for way in ways:
|
||||
backtrace(way,path)
|
||||
path.pop()
|
||||
backtrace(root,path)
|
||||
return ["->".join(list(map(str,i))) for i in res]
|
||||
path, result = '', []
|
||||
self.traversal(root, path, result)
|
||||
return result
|
||||
|
||||
def traversal(self, cur: TreeNode, path: List, result: List):
|
||||
path += str(cur.val)
|
||||
# 如果当前节点为叶子节点,添加路径到结果中
|
||||
if not (cur.left or cur.right):
|
||||
result.append(path)
|
||||
return
|
||||
|
||||
if cur.left:
|
||||
self.traversal(cur.left, path + '->', result)
|
||||
|
||||
if cur.right:
|
||||
self.traversal(cur.right, path + '->', result)
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Solution:
|
||||
"""二叉树的所有路径 迭代法"""
|
||||
|
||||
def binaryTreePaths(self, root: TreeNode) -> List[str]:
|
||||
# 题目中节点数至少为1
|
||||
stack, path_st, result = deque([root]), deque(), []
|
||||
path_st.append(str(root.val))
|
||||
|
||||
while stack:
|
||||
cur = stack.pop()
|
||||
path = path_st.pop()
|
||||
# 如果当前节点为叶子节点,添加路径到结果中
|
||||
if not (cur.left or cur.right):
|
||||
result.append(path)
|
||||
if cur.right:
|
||||
stack.append(cur.right)
|
||||
path_st.append(path + '->' + str(cur.right.val))
|
||||
if cur.left:
|
||||
stack.append(cur.left)
|
||||
path_st.append(path + '->' + str(cur.left.val))
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
```go
|
||||
func binaryTreePaths(root *TreeNode) []string {
|
||||
|
@ -206,6 +206,7 @@ var reverseString = function(s) {
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
// 双指针 - 元组
|
||||
func reverseString(_ s: inout [Character]) {
|
||||
var l = 0
|
||||
var r = s.count - 1
|
||||
@ -216,11 +217,18 @@ func reverseString(_ s: inout [Character]) {
|
||||
r -= 1
|
||||
}
|
||||
}
|
||||
|
||||
// 双指针法 - 库函数
|
||||
func reverseString(_ s: inout [Character]) {
|
||||
var j = s.count - 1
|
||||
for i in 0 ..< Int(Double(s.count) * 0.5) {
|
||||
s.swapAt(i, j)
|
||||
j -= 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -238,6 +238,25 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
use std::collections::HashSet;
|
||||
impl Solution {
|
||||
pub fn intersection(nums1: Vec<i32>, nums2: Vec<i32>) -> Vec<i32> {
|
||||
let mut resultSet: HashSet<i32> = HashSet::with_capacity(1000);
|
||||
let nums1Set: HashSet<i32> = nums1.into_iter().collect();
|
||||
|
||||
for num in nums2.iter() {
|
||||
if nums1Set.contains(num) {
|
||||
resultSet.insert(*num);
|
||||
}
|
||||
}
|
||||
|
||||
let ret: Vec<i32> = resultSet.into_iter().collect();
|
||||
ret
|
||||
}
|
||||
}
|
||||
```
|
||||
## 相关题目
|
||||
|
||||
* 350.两个数组的交集 II
|
||||
|
@ -315,6 +315,28 @@ func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
pub fn can_construct(ransom_note: String, magazine: String) -> bool {
|
||||
let baseChar = 'a';
|
||||
let mut record = vec![0; 26];
|
||||
|
||||
for byte in magazine.bytes() {
|
||||
record[byte as usize - baseChar as usize] += 1;
|
||||
}
|
||||
|
||||
for byte in ransom_note.bytes() {
|
||||
record[byte as usize - baseChar as usize] -= 1;
|
||||
if record[byte as usize - baseChar as usize] < 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -227,6 +227,45 @@ class Solution:
|
||||
```
|
||||
Go:
|
||||
|
||||
```
|
||||
func canPartition(nums []int) bool {
|
||||
/**
|
||||
动态五部曲:
|
||||
1.确定dp数组和下标含义
|
||||
2.确定递推公式
|
||||
3.dp数组初始化
|
||||
4.dp遍历顺序
|
||||
5.打印
|
||||
**/
|
||||
//确定和
|
||||
var sum int
|
||||
for _,v:=range nums{
|
||||
sum+=v
|
||||
}
|
||||
if sum%2!=0{ //如果和为奇数,则不可能分成两个相等的数组
|
||||
return false
|
||||
}
|
||||
sum/=2
|
||||
//确定dp数组和下标含义
|
||||
var dp [][]bool //dp[i][j] 表示: 前i个石头是否总和不大于J
|
||||
//初始化数组
|
||||
dp=make([][]bool,len(nums)+1)
|
||||
for i,_:=range dp{
|
||||
dp[i]=make([]bool,sum+1)
|
||||
dp[i][0]=true
|
||||
}
|
||||
for i:=1;i<=len(nums);i++{
|
||||
for j:=1;j<=sum;j++{//j是固定总量
|
||||
if j>=nums[i-1]{//如果容量够用则可放入背包
|
||||
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]]
|
||||
}else{//如果容量不够用则不拿,维持前一个状态
|
||||
dp[i][j]=dp[i-1][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(nums)][sum]
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
|
@ -274,6 +274,7 @@ func min(a,b int)int{
|
||||
}
|
||||
```
|
||||
Javascript:
|
||||
- 按右边界排序
|
||||
```Javascript
|
||||
var eraseOverlapIntervals = function(intervals) {
|
||||
intervals.sort((a, b) => {
|
||||
@ -285,7 +286,7 @@ var eraseOverlapIntervals = function(intervals) {
|
||||
|
||||
for(let i = 1; i < intervals.length; i++) {
|
||||
let interval = intervals[i]
|
||||
if(interval[0] >= right) {
|
||||
if(interval[0] >= end) {
|
||||
end = interval[1]
|
||||
count += 1
|
||||
}
|
||||
@ -294,6 +295,24 @@ var eraseOverlapIntervals = function(intervals) {
|
||||
return intervals.length - count
|
||||
};
|
||||
```
|
||||
- 按左边界排序
|
||||
```js
|
||||
var eraseOverlapIntervals = function(intervals) {
|
||||
// 按照左边界升序排列
|
||||
intervals.sort((a, b) => a[0] - b[0])
|
||||
let count = 1
|
||||
let end = intervals[intervals.length - 1][0]
|
||||
// 倒序遍历,对单个区间来说,左边界越大越好,因为给前面区间的空间越大
|
||||
for(let i = intervals.length - 2; i >= 0; i--) {
|
||||
if(intervals[i][1] <= end) {
|
||||
count++
|
||||
end = intervals[i][0]
|
||||
}
|
||||
}
|
||||
// count 记录的是最大非重复区间的个数
|
||||
return intervals.length - count
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -282,6 +282,31 @@ func fourSumCount(_ nums1: [Int], _ nums2: [Int], _ nums3: [Int], _ nums4: [Int]
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
impl Solution {
|
||||
pub fn four_sum_count(nums1: Vec<i32>, nums2: Vec<i32>, nums3: Vec<i32>, nums4: Vec<i32>) -> i32 {
|
||||
let mut umap:HashMap<i32, i32> = HashMap::new();
|
||||
for num1 in &nums1 {
|
||||
for num2 in &nums2 {
|
||||
*umap.entry(num1 + num2).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
for num3 in &nums3 {
|
||||
for num4 in &nums4 {
|
||||
let target:i32 = - (num3 + num4);
|
||||
count += umap.get(&target).unwrap_or(&0);
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -84,6 +84,37 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
```java
|
||||
// 解法一
|
||||
class Solution {
|
||||
// 上下左右 4 个方向
|
||||
int[] dirx = {-1, 1, 0, 0};
|
||||
int[] diry = {0, 0, -1, 1};
|
||||
|
||||
public int islandPerimeter(int[][] grid) {
|
||||
int m = grid.length;
|
||||
int n = grid[0].length;
|
||||
int res = 0; // 岛屿周长
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (grid[i][j] == 1) {
|
||||
for (int k = 0; k < 4; k++) {
|
||||
int x = i + dirx[k];
|
||||
int y = j + diry[k];
|
||||
// 当前位置是陆地,并且从当前位置4个方向扩展的“新位置”是“水域”或“新位置“越界,则会为周长贡献一条边
|
||||
if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == 0) {
|
||||
res++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
Go:
|
||||
|
@ -248,6 +248,53 @@ func max(a,b int) int {
|
||||
return b
|
||||
}
|
||||
```
|
||||
> 传统背包,三维数组法
|
||||
```golang
|
||||
func findMaxForm(strs []string, m int, n int) int {
|
||||
//dp的第一个index代表项目的多少,第二个代表的是背包的容量
|
||||
//所以本处项目的多少是len(strs),容量为m和n
|
||||
dp:=make([][][]int,len(strs)+1)
|
||||
for i:=0;i<=len(strs);i++{
|
||||
//初始化背包容量
|
||||
strDp:=make([][]int,m+1)
|
||||
for j:=0;j<m+1;j++{
|
||||
tmp:=make([]int,n+1)
|
||||
strDp[j]=tmp
|
||||
}
|
||||
dp[i]=strDp
|
||||
}
|
||||
for k,value:=range strs{
|
||||
//统计每个字符串0和1的个数
|
||||
var zero,one int
|
||||
for _,v:=range value{
|
||||
if v=='0'{
|
||||
zero++
|
||||
}else{
|
||||
one++
|
||||
}
|
||||
}
|
||||
k+=1
|
||||
//计算dp
|
||||
for i:=0;i<=m;i++{
|
||||
for j:=0;j<=n;j++{
|
||||
//如果装不下
|
||||
dp[k][i][j]=dp[k-1][i][j]
|
||||
//如果装的下
|
||||
if i>=zero&&j>=one{
|
||||
dp[k][i][j]=getMax(dp[k-1][i][j],dp[k-1][i-zero][j-one]+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(strs)][m][n]
|
||||
}
|
||||
func getMax(a,b int)int{
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
|
@ -314,6 +314,47 @@ func findTargetSumWays(nums []int, target int) int {
|
||||
return dp[bag]
|
||||
}
|
||||
```
|
||||
> 更新版,上一个跑不通了,因为会存在bag 小于0的情况
|
||||
|
||||
```go
|
||||
func findTargetSumWays(nums []int, target int) int {
|
||||
//先转化为数学问题
|
||||
//a-b=target
|
||||
//a+b=sum
|
||||
//a=(target+sum)/2
|
||||
//求出sum
|
||||
var sum int
|
||||
for _,value:=range nums{
|
||||
sum+=value
|
||||
}
|
||||
//如果sum<target或者 sum+target不是偶数(因为a是int) 或者两者之和小于0了
|
||||
if sum<target||(sum+target)%2==1||(sum+target)<0{
|
||||
return 0
|
||||
}
|
||||
//开始dp初始化
|
||||
dp:=make([][]int,len(nums)+1)
|
||||
for i:=0;i<=len(nums);i++{
|
||||
tmp:=make([]int,(target+sum)/2+1)//背包容量
|
||||
dp[i]=tmp
|
||||
}
|
||||
dp[0][0]=1//当背包容量为0,且物品为0时,填满背包就1种方法
|
||||
for i:=0;i<len(nums)+1;i++{
|
||||
if i==0{
|
||||
continue
|
||||
}
|
||||
for j:=0;j<(target+sum)/2+1;j++{
|
||||
if nums[i-1]<=j{//如果背包装的下
|
||||
dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]]
|
||||
}else{
|
||||
dp[i][j]=dp[i-1][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(nums)][(target+sum)/2]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
@ -321,7 +362,7 @@ const findTargetSumWays = (nums, target) => {
|
||||
|
||||
const sum = nums.reduce((a, b) => a+b);
|
||||
|
||||
if(target > sum) {
|
||||
if(Math.abs(target) > sum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -238,4 +238,37 @@ class Solution:
|
||||
return result
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
func nextGreaterElement(nums1 []int, nums2 []int) []int {
|
||||
res := make([]int, len(nums1))
|
||||
for i:= range res {
|
||||
res[i] = -1
|
||||
}
|
||||
mp := map[int]int{}
|
||||
for i,v := range nums1 {
|
||||
mp[v] = i
|
||||
}
|
||||
// 单调栈
|
||||
stack := []int{}
|
||||
stack = append(stack,0)
|
||||
|
||||
for i:=1; i<len(nums2); i++ {
|
||||
for len(stack) >0 && nums2[i] > nums2[stack[len(stack)-1]] {
|
||||
|
||||
top := stack[len(stack)-1]
|
||||
|
||||
if _, ok := mp[nums2[top]]; ok { // 看map里是否存在这个元素
|
||||
index := mp[nums2[top]]; // 根据map找到nums2[top] 在 nums1中的下表
|
||||
res[index] = nums2[i]
|
||||
}
|
||||
|
||||
stack = stack[:len(stack)-1] // 出栈
|
||||
}
|
||||
stack = append(stack, i)
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
# 513.找树左下角的值
|
||||
|
||||
[力扣题目链接]([https://leetcode-cn.com/problems/find-bottom-left-tree-value/](https://leetcode-cn.com/problems/find-bottom-left-tree-value/v))
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/find-bottom-left-tree-value/)
|
||||
|
||||
给定一个二叉树,在树的最后一行找到最左边的值。
|
||||
|
||||
|
@ -258,16 +258,30 @@ class Solution {
|
||||
## Python
|
||||
|
||||
```python
|
||||
//递归法
|
||||
class Solution:
|
||||
"""最大二叉树 递归法"""
|
||||
|
||||
def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
|
||||
if not nums: return None //终止条件
|
||||
root = TreeNode(max(nums)) //新建节点
|
||||
p = nums.index(root.val) //找到最大值位置
|
||||
if p > 0: //保证有左子树
|
||||
root.left = self.constructMaximumBinaryTree(nums[:p]) //递归
|
||||
if p < len(nums): //保证有右子树
|
||||
root.right = self.constructMaximumBinaryTree(nums[p+1:]) //递归
|
||||
return self.traversal(nums, 0, len(nums))
|
||||
|
||||
def traversal(self, nums: List[int], begin: int, end: int) -> TreeNode:
|
||||
# 列表长度为0时返回空节点
|
||||
if begin == end:
|
||||
return None
|
||||
|
||||
# 找到最大的值和其对应的下标
|
||||
max_index = begin
|
||||
for i in range(begin, end):
|
||||
if nums[i] > nums[max_index]:
|
||||
max_index = i
|
||||
|
||||
# 构建当前节点
|
||||
root = TreeNode(nums[max_index])
|
||||
|
||||
# 递归构建左右子树
|
||||
root.left = self.traversal(nums, begin, max_index)
|
||||
root.right = self.traversal(nums, max_index + 1, end)
|
||||
|
||||
return root
|
||||
```
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
# 684.冗余连接
|
||||
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/redundant-connection/)
|
||||
|
||||
树可以看成是一个连通且 无环 的 无向 图。
|
||||
|
||||
给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
|
||||
@ -140,16 +143,161 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
private int n; // 节点数量3 到 1000
|
||||
private int[] father;
|
||||
public Solution() {
|
||||
n = 1005;
|
||||
father = new int[n];
|
||||
|
||||
// 并查集初始化
|
||||
for (int i = 0; i < n; ++i) {
|
||||
father[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集里寻根的过程
|
||||
private int find(int u) {
|
||||
if(u == father[u]) {
|
||||
return u;
|
||||
}
|
||||
father[u] = find(father[u]);
|
||||
return father[u];
|
||||
}
|
||||
|
||||
// 将v->u 这条边加入并查集
|
||||
private void join(int u, int v) {
|
||||
u = find(u);
|
||||
v = find(v);
|
||||
if (u == v) return ;
|
||||
father[v] = u;
|
||||
}
|
||||
|
||||
// 判断 u 和 v是否找到同一个根,本题用不上
|
||||
private Boolean same(int u, int v) {
|
||||
u = find(u);
|
||||
v = find(v);
|
||||
return u == v;
|
||||
}
|
||||
|
||||
public int[] findRedundantConnection(int[][] edges) {
|
||||
for (int i = 0; i < edges.length; i++) {
|
||||
if (same(edges[i][0], edges[i][1])) {
|
||||
return edges[i];
|
||||
} else {
|
||||
join(edges[i][0], edges[i][1]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
|
||||
class Solution:
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化
|
||||
"""
|
||||
self.n = 1005
|
||||
self.father = [i for i in range(self.n)]
|
||||
|
||||
|
||||
def find(self, u):
|
||||
"""
|
||||
并查集里寻根的过程
|
||||
"""
|
||||
if u == self.father[u]:
|
||||
return u
|
||||
self.father[u] = self.find(self.father[u])
|
||||
return self.father[u]
|
||||
|
||||
def join(self, u, v):
|
||||
"""
|
||||
将v->u 这条边加入并查集
|
||||
"""
|
||||
u = self.find(u)
|
||||
v = self.find(v)
|
||||
if u == v : return
|
||||
self.father[v] = u
|
||||
pass
|
||||
|
||||
|
||||
def same(self, u, v ):
|
||||
"""
|
||||
判断 u 和 v是否找到同一个根,本题用不上
|
||||
"""
|
||||
u = self.find(u)
|
||||
v = self.find(v)
|
||||
return u == v
|
||||
|
||||
def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
|
||||
for i in range(len(edges)):
|
||||
if self.same(edges[i][0], edges[i][1]) :
|
||||
return edges[i]
|
||||
else :
|
||||
self.join(edges[i][0], edges[i][1])
|
||||
return []
|
||||
```
|
||||
|
||||
## Go
|
||||
|
||||
```go
|
||||
|
||||
// 全局变量
|
||||
var (
|
||||
n = 1005 // 节点数量3 到 1000
|
||||
father = make([]int, 1005)
|
||||
)
|
||||
|
||||
// 并查集初始化
|
||||
func initialize() {
|
||||
for i := 0; i < n; i++ {
|
||||
father[i] = i
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集里寻根的过程
|
||||
func find(u int) int {
|
||||
if u == father[u] {
|
||||
return u
|
||||
}
|
||||
father[u] = find(father[u])
|
||||
return father[u]
|
||||
}
|
||||
|
||||
// 将v->u 这条边加入并查集
|
||||
func join(u, v int) {
|
||||
u = find(u)
|
||||
v = find(v)
|
||||
if u == v {
|
||||
return
|
||||
}
|
||||
father[v] = u
|
||||
}
|
||||
|
||||
// 判断 u 和 v是否找到同一个根,本题用不上
|
||||
func same(u, v int) bool {
|
||||
u = find(u)
|
||||
v = find(v)
|
||||
return u == v
|
||||
}
|
||||
|
||||
func findRedundantConnection(edges [][]int) []int {
|
||||
initialize()
|
||||
for i := 0; i < len(edges); i++ {
|
||||
if same(edges[i][0], edges[i][1]) {
|
||||
return edges[i]
|
||||
} else {
|
||||
join(edges[i][0], edges[i][1])
|
||||
}
|
||||
}
|
||||
return []int{}
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
**这说明题目中的图原本是是一棵树,只不过在不增加节点的情况下多加了一条边!**
|
||||
|
||||
还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两天边都可以删除的情况下,要删顺序靠后的!
|
||||
还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两条边都可以删除的情况下,要删顺序靠后的!
|
||||
|
||||
|
||||
那么有如下三种情况,前两种情况是出现入度为2的点,如图:
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
首先先计算节点的入度,代码如下:
|
||||
|
||||
```CPP
|
||||
```cpp
|
||||
int inDegree[N] = {0}; // 记录节点入度
|
||||
n = edges.size(); // 边的数量
|
||||
for (int i = 0; i < n; i++) {
|
||||
@ -70,7 +70,7 @@ for (int i = 0; i < n; i++) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
```cpp
|
||||
vector<int> vec; // 记录入度为2的边(如果有的话就两条边)
|
||||
// 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
@ -112,7 +112,7 @@ vector<int> getRemoveEdge(const vector<vector<int>>& edges)
|
||||
本题C++代码如下:(详细注释了)
|
||||
|
||||
|
||||
```CPP
|
||||
```cpp
|
||||
class Solution {
|
||||
private:
|
||||
static const int N = 1010; // 如题:二维数组大小的在3到1000范围内
|
||||
@ -174,7 +174,7 @@ public:
|
||||
inDegree[edges[i][1]]++; // 统计入度
|
||||
}
|
||||
vector<int> vec; // 记录入度为2的边(如果有的话就两条边)
|
||||
// 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
|
||||
// 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
if (inDegree[edges[i][1]] == 2) {
|
||||
vec.push_back(i);
|
||||
@ -203,16 +203,313 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
|
||||
class Solution {
|
||||
|
||||
private static final int N = 1010; // 如题:二维数组大小的在3到1000范围内
|
||||
private int[] father;
|
||||
public Solution() {
|
||||
father = new int[N];
|
||||
|
||||
// 并查集初始化
|
||||
for (int i = 0; i < N; ++i) {
|
||||
father[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集里寻根的过程
|
||||
private int find(int u) {
|
||||
if(u == father[u]) {
|
||||
return u;
|
||||
}
|
||||
father[u] = find(father[u]);
|
||||
return father[u];
|
||||
}
|
||||
|
||||
// 将v->u 这条边加入并查集
|
||||
private void join(int u, int v) {
|
||||
u = find(u);
|
||||
v = find(v);
|
||||
if (u == v) return ;
|
||||
father[v] = u;
|
||||
}
|
||||
|
||||
// 判断 u 和 v是否找到同一个根,本题用不上
|
||||
private Boolean same(int u, int v) {
|
||||
u = find(u);
|
||||
v = find(v);
|
||||
return u == v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化并查集
|
||||
*/
|
||||
private void initFather() {
|
||||
// 并查集初始化
|
||||
for (int i = 0; i < N; ++i) {
|
||||
father[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在有向图里找到删除的那条边,使其变成树
|
||||
* @param edges
|
||||
* @return 要删除的边
|
||||
*/
|
||||
private int[] getRemoveEdge(int[][] edges) {
|
||||
initFather();
|
||||
for(int i = 0; i < edges.length; i++) {
|
||||
if(same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
|
||||
return edges[i];
|
||||
}
|
||||
join(edges[i][0], edges[i][1]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删一条边之后判断是不是树
|
||||
* @param edges
|
||||
* @param deleteEdge 要删除的边
|
||||
* @return true: 是树, false: 不是树
|
||||
*/
|
||||
private Boolean isTreeAfterRemoveEdge(int[][] edges, int deleteEdge)
|
||||
{
|
||||
initFather();
|
||||
for(int i = 0; i < edges.length; i++)
|
||||
{
|
||||
if(i == deleteEdge) continue;
|
||||
if(same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树
|
||||
return false;
|
||||
}
|
||||
join(edges[i][0], edges[i][1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int[] findRedundantDirectedConnection(int[][] edges) {
|
||||
int[] inDegree = new int[N];
|
||||
for(int i = 0; i < edges.length; i++)
|
||||
{
|
||||
// 入度
|
||||
inDegree[ edges[i][1] ] += 1;
|
||||
}
|
||||
|
||||
// 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
|
||||
ArrayList<Integer> twoDegree = new ArrayList<Integer>();
|
||||
for(int i = edges.length - 1; i >= 0; i--)
|
||||
{
|
||||
if(inDegree[edges[i][1]] == 2) {
|
||||
twoDegree.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图中情况1 和 情况2
|
||||
// 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
|
||||
if(!twoDegree.isEmpty())
|
||||
{
|
||||
if(isTreeAfterRemoveEdge(edges, twoDegree.get(0))) {
|
||||
return edges[ twoDegree.get(0)];
|
||||
}
|
||||
return edges[ twoDegree.get(1)];
|
||||
}
|
||||
|
||||
// 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
|
||||
return getRemoveEdge(edges);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
|
||||
class Solution:
|
||||
|
||||
def __init__(self):
|
||||
self.n = 1010
|
||||
self.father = [i for i in range(self.n)]
|
||||
|
||||
|
||||
def find(self, u: int):
|
||||
"""
|
||||
并查集里寻根的过程
|
||||
"""
|
||||
if u == self.father[u]:
|
||||
return u
|
||||
self.father[u] = self.find(self.father[u])
|
||||
return self.father[u]
|
||||
|
||||
def join(self, u: int, v: int):
|
||||
"""
|
||||
将v->u 这条边加入并查集
|
||||
"""
|
||||
u = self.find(u)
|
||||
v = self.find(v)
|
||||
if u == v : return
|
||||
self.father[v] = u
|
||||
pass
|
||||
|
||||
|
||||
def same(self, u: int, v: int ):
|
||||
"""
|
||||
判断 u 和 v是否找到同一个根,本题用不上
|
||||
"""
|
||||
u = self.find(u)
|
||||
v = self.find(v)
|
||||
return u == v
|
||||
|
||||
def init_father(self):
|
||||
self.father = [i for i in range(self.n)]
|
||||
pass
|
||||
|
||||
def getRemoveEdge(self, edges: List[List[int]]) -> List[int]:
|
||||
"""
|
||||
在有向图里找到删除的那条边,使其变成树
|
||||
"""
|
||||
|
||||
self.init_father()
|
||||
for i in range(len(edges)):
|
||||
if self.same(edges[i][0], edges[i][1]): # 构成有向环了,就是要删除的边
|
||||
return edges[i]
|
||||
self.join(edges[i][0], edges[i][1]);
|
||||
return []
|
||||
|
||||
def isTreeAfterRemoveEdge(self, edges: List[List[int]], deleteEdge: int) -> bool:
|
||||
"""
|
||||
删一条边之后判断是不是树
|
||||
"""
|
||||
|
||||
self.init_father()
|
||||
for i in range(len(edges)):
|
||||
if i == deleteEdge: continue
|
||||
if self.same(edges[i][0], edges[i][1]): # 构成有向环了,一定不是树
|
||||
return False
|
||||
self.join(edges[i][0], edges[i][1]);
|
||||
return True
|
||||
|
||||
def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]:
|
||||
inDegree = [0 for i in range(self.n)]
|
||||
|
||||
for i in range(len(edges)):
|
||||
inDegree[ edges[i][1] ] += 1
|
||||
|
||||
# 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
|
||||
towDegree = []
|
||||
for i in range(len(edges))[::-1]:
|
||||
if inDegree[edges[i][1]] == 2 :
|
||||
towDegree.append(i)
|
||||
|
||||
# 处理图中情况1 和 情况2
|
||||
# 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
|
||||
if len(towDegree) > 0:
|
||||
if(self.isTreeAfterRemoveEdge(edges, towDegree[0])) :
|
||||
return edges[towDegree[0]]
|
||||
return edges[towDegree[1]]
|
||||
|
||||
# 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
|
||||
return self.getRemoveEdge(edges)
|
||||
```
|
||||
|
||||
## Go
|
||||
|
||||
```go
|
||||
|
||||
// 全局变量
|
||||
var (
|
||||
n = 1010// 节点数量3 到 1000
|
||||
father = make([]int, n)
|
||||
)
|
||||
|
||||
// 并查集初始化
|
||||
func initialize() {
|
||||
for i := 0; i < n; i++ {
|
||||
father[i] = i
|
||||
}
|
||||
}
|
||||
|
||||
// 并查集里寻根的过程
|
||||
func find(u int) int {
|
||||
if u == father[u] {
|
||||
return u
|
||||
}
|
||||
father[u] = find(father[u])
|
||||
return father[u]
|
||||
}
|
||||
|
||||
// 将v->u 这条边加入并查集
|
||||
func join(u, v int) {
|
||||
u = find(u)
|
||||
v = find(v)
|
||||
if u == v {
|
||||
return
|
||||
}
|
||||
father[v] = u
|
||||
}
|
||||
|
||||
// 判断 u 和 v是否找到同一个根,本题用不上
|
||||
func same(u, v int) bool {
|
||||
u = find(u)
|
||||
v = find(v)
|
||||
return u == v
|
||||
}
|
||||
|
||||
// getRemoveEdge 在有向图里找到删除的那条边,使其变成树
|
||||
func getRemoveEdge(edges [][]int) []int {
|
||||
initialize()
|
||||
for i := 0; i < len(edges); i++ { // 遍历所有的边
|
||||
if same(edges[i][0], edges[i][1]) { // 构成有向环了,就是要删除的边
|
||||
return edges[i]
|
||||
}
|
||||
join(edges[i][0], edges[i][1])
|
||||
}
|
||||
return []int{}
|
||||
}
|
||||
|
||||
// isTreeAfterRemoveEdge 删一条边之后判断是不是树
|
||||
func isTreeAfterRemoveEdge(edges [][]int, deleteEdge int) bool {
|
||||
initialize()
|
||||
for i := 0; i < len(edges); i++ {
|
||||
if i == deleteEdge {
|
||||
continue
|
||||
}
|
||||
if same(edges[i][0], edges[i][1]) { // 构成有向环了,一定不是树
|
||||
return false
|
||||
}
|
||||
join(edges[i][0], edges[i][1])
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func findRedundantDirectedConnection(edges [][]int) []int {
|
||||
inDegree := make([]int, len(father))
|
||||
for i := 0; i < len(edges); i++ {
|
||||
// 统计入度
|
||||
inDegree[edges[i][1]] += 1
|
||||
}
|
||||
// 记录入度为2的边(如果有的话就两条边)
|
||||
// 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
|
||||
twoDegree := make([]int, 0)
|
||||
for i := len(edges) - 1; i >= 0; i-- {
|
||||
if inDegree[edges[i][1]] == 2 {
|
||||
twoDegree = append(twoDegree, i)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图中情况1 和 情况2
|
||||
// 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
|
||||
if len(twoDegree) > 0 {
|
||||
if isTreeAfterRemoveEdge(edges, twoDegree[0]) {
|
||||
return edges[twoDegree[0]]
|
||||
}
|
||||
return edges[twoDegree[1]]
|
||||
}
|
||||
|
||||
// 处理图中情况3
|
||||
// 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
|
||||
return getRemoveEdge(edges)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
# 724.寻找数组的中心下标
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/find-pivot-index/)
|
||||
|
||||
给你一个整数数组 nums ,请计算数组的 中心下标 。
|
||||
|
||||
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
|
||||
@ -87,15 +89,15 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
## Python3
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def pivotIndex(self, nums: List[int]) -> int:
|
||||
numSum = sum(nums) #数组总和
|
||||
leftSum = 0
|
||||
for i in range(len(nums)):
|
||||
if numSum - leftSum -nums[i] == leftSum: #左右和相等
|
||||
if numSum - leftSum -nums[i] == leftSum: #左右和相等
|
||||
return i
|
||||
leftSum += nums[i]
|
||||
return -1
|
||||
@ -104,6 +106,24 @@ class Solution:
|
||||
## Go
|
||||
|
||||
```go
|
||||
func pivotIndex(nums []int) int {
|
||||
sum := 0
|
||||
for _, v := range nums {
|
||||
sum += v;
|
||||
}
|
||||
|
||||
leftSum := 0 // 中心索引左半和
|
||||
rightSum := 0 // 中心索引右半和
|
||||
for i := 0; i < len(nums); i++ {
|
||||
leftSum += nums[i]
|
||||
rightSum = sum - leftSum + nums[i]
|
||||
if leftSum == rightSum{
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
@ -347,24 +347,57 @@ class Solution {
|
||||
|
||||
Python:
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def minCameraCover(self, root: TreeNode) -> int:
|
||||
# Greedy Algo:
|
||||
# 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
|
||||
# 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
|
||||
# 0: 该节点未覆盖
|
||||
# 1: 该节点有摄像头
|
||||
# 2: 该节点有覆盖
|
||||
|
||||
result = 0
|
||||
def traversal(cur):
|
||||
# 从下往上遍历:后序(左右中)
|
||||
def traversal(curr: TreeNode) -> int:
|
||||
nonlocal result
|
||||
if not cur:
|
||||
return 2
|
||||
left = traversal(cur.left)
|
||||
right = traversal(cur.right)
|
||||
if left == 2 and right == 2:
|
||||
|
||||
if not curr: return 2
|
||||
left = traversal(curr.left)
|
||||
right = traversal(curr.right)
|
||||
|
||||
# Case 1:
|
||||
# 左右节点都有覆盖
|
||||
if left == 2 and right == 2:
|
||||
return 0
|
||||
elif left == 0 or right == 0:
|
||||
|
||||
# Case 2:
|
||||
# left == 0 && right == 0 左右节点无覆盖
|
||||
# left == 1 && right == 0 左节点有摄像头,右节点无覆盖
|
||||
# left == 0 && right == 1 左节点有无覆盖,右节点摄像头
|
||||
# left == 0 && right == 2 左节点无覆盖,右节点覆盖
|
||||
# left == 2 && right == 0 左节点覆盖,右节点无覆盖
|
||||
elif left == 0 or right == 0:
|
||||
result += 1
|
||||
return 1
|
||||
|
||||
# Case 3:
|
||||
# left == 1 && right == 2 左节点有摄像头,右节点有覆盖
|
||||
# left == 2 && right == 1 左节点有覆盖,右节点有摄像头
|
||||
# left == 1 && right == 1 左右节点都有摄像头
|
||||
elif left == 1 or right == 1:
|
||||
return 2
|
||||
else: return -1
|
||||
if traversal(root) == 0: result += 1
|
||||
|
||||
# 其他情况前段代码均已覆盖
|
||||
|
||||
if traversal(root) == 0:
|
||||
result += 1
|
||||
|
||||
return result
|
||||
```
|
||||
Go:
|
||||
|
@ -219,7 +219,28 @@ func max(a, b int) int {
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript版本
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @param {number[]} stones
|
||||
* @return {number}
|
||||
*/
|
||||
var lastStoneWeightII = function (stones) {
|
||||
let sum = stones.reduce((s, n) => s + n);
|
||||
|
||||
let dpLen = Math.floor(sum / 2);
|
||||
let dp = new Array(dpLen + 1).fill(0);
|
||||
|
||||
for (let i = 0; i < stones.length; ++i) {
|
||||
for (let j = dpLen; j >= stones[i]; --j) {
|
||||
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sum - dp[dpLen] - dp[dpLen];
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -167,7 +167,33 @@ else {
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int minDistance(String word1, String word2) {
|
||||
int m = word1.length();
|
||||
int n = word2.length();
|
||||
int[][] dp = new int[m+1][n+1];
|
||||
for(int i = 1; i <= m; i++){
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for(int i = 1; i <= n; i++){
|
||||
dp[0][i] = i;
|
||||
}
|
||||
for(int i = 1; i <= m; i++){
|
||||
for(int j = 1; j <= n; j++){
|
||||
int left = dp[i][j-1]+1;
|
||||
int mid = dp[i-1][j-1];
|
||||
int right = dp[i-1][j]+1;
|
||||
if(word1.charAt(i-1) != word2.charAt(j-1)){
|
||||
mid ++;
|
||||
}
|
||||
dp[i][j] = Math.min(left,Math.min(mid,right));
|
||||
}
|
||||
}
|
||||
return dp[m][n];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -34,7 +34,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
||||
|
||||
## 01 背包
|
||||
|
||||
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
|
||||
有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
|
||||
|
||||

|
||||
|
||||
|
Reference in New Issue
Block a user