mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 00:43:04 +08:00
Merge branch 'master' into master
This commit is contained in:
@ -28,6 +28,8 @@
|
||||
|
||||
## 思路
|
||||
|
||||
[本题B站视频讲解](https://www.bilibili.com/video/BV12A4y1Z7LP)
|
||||
|
||||
有的同学可能说了,多余的元素,删掉不就得了。
|
||||
|
||||
**要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。**
|
||||
@ -75,10 +77,20 @@ public:
|
||||
|
||||
双指针法(快慢指针法): **通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。**
|
||||
|
||||
定义快慢指针
|
||||
|
||||
* 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
|
||||
* 慢指针:指向更新 新数组下标的位置
|
||||
|
||||
很多同学这道题目做的很懵,就是不理解 快慢指针究竟都是什么含义,所以一定要明确含义,后面的思路就更容易理解了。
|
||||
|
||||
删除过程如下:
|
||||
|
||||

|
||||
|
||||
很多同学不了解
|
||||
|
||||
|
||||
**双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。**
|
||||
|
||||
后续都会一一介绍到,本题代码如下:
|
||||
@ -104,8 +116,6 @@ public:
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
|
||||
|
||||
```CPP
|
||||
/**
|
||||
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
|
||||
@ -329,6 +339,17 @@ int removeElement(int* nums, int numsSize, int val){
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
fun removeElement(nums: IntArray, `val`: Int): Int {
|
||||
var slowIndex = 0 // 初始化慢指针
|
||||
for (fastIndex in nums.indices) {
|
||||
if (nums[fastIndex] != `val`) nums[slowIndex++] = nums[fastIndex] // 在慢指针所在位置存储未被删除的元素
|
||||
}
|
||||
return slowIndex
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
@ -359,5 +380,6 @@ public class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -73,8 +73,8 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
效率如下:
|
||||
|
||||
@ -135,14 +135,14 @@ public:
|
||||
// 目标值在数组所有元素之前 [0, -1]
|
||||
// 目标值等于数组中某一个元素 return middle;
|
||||
// 目标值插入数组中的位置 [left, right],return right + 1
|
||||
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
|
||||
// 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
|
||||
return right + 1;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(\log n)$
|
||||
* 时间复杂度:$O(1)$
|
||||
* 时间复杂度:O(log n)
|
||||
* 时间复杂度:O(1)
|
||||
|
||||
效率如下:
|
||||

|
||||
@ -178,7 +178,7 @@ public:
|
||||
// 目标值在数组所有元素之前 [0,0)
|
||||
// 目标值等于数组中某一个元素 return middle
|
||||
// 目标值插入数组中的位置 [left, right) ,return right 即可
|
||||
// 目标值在数组所有元素之后的情况 [left, right),return right 即可
|
||||
// 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
|
||||
return right;
|
||||
}
|
||||
};
|
||||
|
@ -744,6 +744,91 @@ var trap = function(height) {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
双指针法:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
let resVal: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let leftMaxHeight: number = height[i],
|
||||
rightMaxHeight: number = height[i];
|
||||
let leftIndex: number = i - 1,
|
||||
rightIndex: number = i + 1;
|
||||
while (leftIndex >= 0) {
|
||||
if (height[leftIndex] > leftMaxHeight)
|
||||
leftMaxHeight = height[leftIndex];
|
||||
leftIndex--;
|
||||
}
|
||||
while (rightIndex < length) {
|
||||
if (height[rightIndex] > rightMaxHeight)
|
||||
rightMaxHeight = height[rightIndex];
|
||||
rightIndex++;
|
||||
}
|
||||
resVal += Math.min(leftMaxHeight, rightMaxHeight) - height[i];
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
动态规划:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
const leftMaxHeightDp: number[] = [],
|
||||
rightMaxHeightDp: number[] = [];
|
||||
leftMaxHeightDp[0] = height[0];
|
||||
rightMaxHeightDp[length - 1] = height[length - 1];
|
||||
for (let i = 1; i < length; i++) {
|
||||
leftMaxHeightDp[i] = Math.max(height[i], leftMaxHeightDp[i - 1]);
|
||||
}
|
||||
for (let i = length - 2; i >= 0; i--) {
|
||||
rightMaxHeightDp[i] = Math.max(height[i], rightMaxHeightDp[i + 1]);
|
||||
}
|
||||
let resVal: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
resVal += Math.min(leftMaxHeightDp[i], rightMaxHeightDp[i]) - height[i];
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
单调栈:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
let resVal: number = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
if (height[top] > height[i]) {
|
||||
stack.push(i);
|
||||
} else if (height[top] === height[i]) {
|
||||
stack.pop();
|
||||
stack.push(i);
|
||||
} else {
|
||||
while (stack.length > 0 && height[top] < height[i]) {
|
||||
let mid = stack.pop();
|
||||
if (stack.length > 0) {
|
||||
let left = stack[stack.length - 1];
|
||||
let h = Math.min(height[left], height[i]) - height[mid];
|
||||
let w = i - left - 1;
|
||||
resVal += h * w;
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
### C:
|
||||
|
||||
一种更简便的双指针方法:
|
||||
|
@ -341,7 +341,7 @@ function permute(nums: number[]): number[][] {
|
||||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
let tempVal: number;
|
||||
|
@ -268,7 +268,7 @@ var permuteUnique = function (nums) {
|
||||
|
||||
function backtracing( used) {
|
||||
if (path.length === nums.length) {
|
||||
result.push(path.slice())
|
||||
result.push([...path])
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
@ -303,7 +303,7 @@ function permuteUnique(nums: number[]): number[][] {
|
||||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
|
@ -44,7 +44,7 @@ n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并
|
||||
# 思路
|
||||
|
||||
|
||||
想看:[51.N皇后](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg) ,基本没有区别
|
||||
详看:[51.N皇后](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg) ,基本没有区别
|
||||
|
||||
# C++代码
|
||||
|
||||
|
@ -186,6 +186,24 @@ const maxSubArray = nums => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxSubArray(nums: number[]): number {
|
||||
/**
|
||||
dp[i]:以nums[i]结尾的最大和
|
||||
*/
|
||||
const dp: number[] = []
|
||||
dp[0] = nums[0];
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i < nums.length; i++) {
|
||||
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
|
||||
resMax = Math.max(resMax, dp[i]);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -66,7 +66,7 @@ dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路
|
||||
|
||||
所以代码为:
|
||||
|
||||
```
|
||||
```cpp
|
||||
if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
|
||||
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
|
||||
}
|
||||
@ -76,7 +76,7 @@ if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i
|
||||
|
||||
在[62.不同路径](https://programmercarl.com/0062.不同路径.html)不同路径中我们给出如下的初始化:
|
||||
|
||||
```
|
||||
```cpp
|
||||
vector<vector<int>> dp(m, vector<int>(n, 0)); // 初始值为0
|
||||
for (int i = 0; i < m; i++) dp[i][0] = 1;
|
||||
for (int j = 0; j < n; j++) dp[0][j] = 1;
|
||||
@ -138,6 +138,8 @@ public:
|
||||
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
|
||||
int m = obstacleGrid.size();
|
||||
int n = obstacleGrid[0].size();
|
||||
if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) //如果在起点或终点出现了障碍,直接返回0
|
||||
return 0;
|
||||
vector<vector<int>> dp(m, vector<int>(n, 0));
|
||||
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
|
||||
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
|
||||
|
@ -327,5 +327,42 @@ const minDistance = (word1, word2) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,最少操作数
|
||||
dp[0][0]=0:表示word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 0; i <= length1; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let i = 0; i <= length2; i++) {
|
||||
dp[0][i] = i;
|
||||
}
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(
|
||||
dp[i - 1][j],
|
||||
dp[i][j - 1],
|
||||
dp[i - 1][j - 1]
|
||||
) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -260,7 +260,7 @@ var subsets = function(nums) {
|
||||
let result = []
|
||||
let path = []
|
||||
function backtracking(startIndex) {
|
||||
result.push(path.slice())
|
||||
result.push([...path])
|
||||
for(let i = startIndex; i < nums.length; i++) {
|
||||
path.push(nums[i])
|
||||
backtracking(i + 1)
|
||||
@ -280,7 +280,7 @@ function subsets(nums: number[]): number[][] {
|
||||
backTracking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTracking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
let length = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
|
@ -486,5 +486,95 @@ var largestRectangleArea = function(heights) {
|
||||
return maxArea;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
> 双指针法(会超时):
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
let resMax: number = 0;
|
||||
for (let i = 0, length = heights.length; i < length; i++) {
|
||||
// 左开右开
|
||||
let left: number = i - 1,
|
||||
right: number = i + 1;
|
||||
while (left >= 0 && heights[left] >= heights[i]) {
|
||||
left--;
|
||||
}
|
||||
while (right < length && heights[right] >= heights[i]) {
|
||||
right++;
|
||||
}
|
||||
resMax = Math.max(resMax, heights[i] * (right - left - 1));
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划预处理:
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
const length: number = heights.length;
|
||||
const leftHeightDp: number[] = [],
|
||||
rightHeightDp: number[] = [];
|
||||
leftHeightDp[0] = -1;
|
||||
rightHeightDp[length - 1] = length;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let j = i - 1;
|
||||
while (j >= 0 && heights[i] <= heights[j]) {
|
||||
j = leftHeightDp[j];
|
||||
}
|
||||
leftHeightDp[i] = j;
|
||||
}
|
||||
for (let i = length - 2; i >= 0; i--) {
|
||||
let j = i + 1;
|
||||
while (j < length && heights[i] <= heights[j]) {
|
||||
j = rightHeightDp[j];
|
||||
}
|
||||
rightHeightDp[i] = j;
|
||||
}
|
||||
let resMax: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let area = heights[i] * (rightHeightDp[i] - leftHeightDp[i] - 1);
|
||||
resMax = Math.max(resMax, area);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 单调栈:
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
heights.push(0);
|
||||
const length: number = heights.length;
|
||||
// 栈底->栈顶:严格单调递增
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
if (heights[top] < heights[i]) {
|
||||
stack.push(i);
|
||||
} else if (heights[top] === heights[i]) {
|
||||
stack.pop();
|
||||
stack.push(i);
|
||||
} else {
|
||||
while (stack.length > 0 && heights[top] > heights[i]) {
|
||||
let mid = stack.pop();
|
||||
let left = stack.length > 0 ? stack[stack.length - 1] : -1;
|
||||
let w = i - left - 1;
|
||||
let h = heights[mid];
|
||||
resMax = Math.max(resMax, w * h);
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -299,7 +299,7 @@ var subsetsWithDup = function(nums) {
|
||||
return a - b
|
||||
})
|
||||
function backtracing(startIndex, sortNums) {
|
||||
result.push(path.slice(0))
|
||||
result.push([...path])
|
||||
if(startIndex > nums.length - 1) {
|
||||
return
|
||||
}
|
||||
@ -327,7 +327,7 @@ function subsetsWithDup(nums: number[]): number[][] {
|
||||
backTraking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTraking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
let length: number = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
|
@ -444,7 +444,7 @@ var restoreIpAddresses = function(s) {
|
||||
return;
|
||||
}
|
||||
for(let j = i; j < s.length; j++) {
|
||||
const str = s.substr(i, j - i + 1);
|
||||
const str = s.slice(i, j + 1);
|
||||
if(str.length > 3 || +str > 255) break;
|
||||
if(str.length > 1 && str[0] === "0") break;
|
||||
path.push(str);
|
||||
|
@ -725,5 +725,25 @@ func isSymmetric3(_ root: TreeNode?) -> Bool {
|
||||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
def isSymmetric(root: TreeNode): Boolean = {
|
||||
if (root == null) return true // 如果等于空直接返回true
|
||||
def compare(left: TreeNode, right: TreeNode): Boolean = {
|
||||
if (left == null && right == null) return true // 如果左右都为空,则为true
|
||||
if (left == null && right != null) return false // 如果左空右不空,不对称,返回false
|
||||
if (left != null && right == null) return false // 如果左不空右空,不对称,返回false
|
||||
// 如果左右的值相等,并且往下递归
|
||||
left.value == right.value && compare(left.left, right.right) && compare(left.right, right.left)
|
||||
}
|
||||
// 分别比较左子树和右子树
|
||||
compare(root.left, root.right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -318,6 +318,31 @@ func levelOrder(_ root: TreeNode?) -> [[Int]] {
|
||||
return result
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
// 102.二叉树的层序遍历
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def levelOrder(root: TreeNode): List[List[Int]] = {
|
||||
val res = mutable.ListBuffer[List[Int]]()
|
||||
if (root == null) return res.toList
|
||||
val queue = mutable.Queue[TreeNode]() // 声明一个队列
|
||||
queue.enqueue(root) // 把根节点加入queue
|
||||
while (!queue.isEmpty) {
|
||||
val tmp = mutable.ListBuffer[Int]()
|
||||
val len = queue.size // 求出len的长度
|
||||
for (i <- 0 until len) { // 从0到当前队列长度的所有节点都加入到结果集
|
||||
val curNode = queue.dequeue()
|
||||
tmp.append(curNode.value)
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
res.append(tmp.toList)
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
|
||||
@ -578,6 +603,32 @@ func levelOrderBottom(_ root: TreeNode?) -> [[Int]] {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 107.二叉树的层次遍历II
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def levelOrderBottom(root: TreeNode): List[List[Int]] = {
|
||||
val res = mutable.ListBuffer[List[Int]]()
|
||||
if (root == null) return res.toList
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val tmp = mutable.ListBuffer[Int]()
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
tmp.append(curNode.value)
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
res.append(tmp.toList)
|
||||
}
|
||||
// 最后翻转一下
|
||||
res.reverse.toList
|
||||
}
|
||||
|
||||
Rust:
|
||||
|
||||
```rust
|
||||
@ -829,6 +880,31 @@ func rightSideView(_ root: TreeNode?) -> [Int] {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 199.二叉树的右视图
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def rightSideView(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
if (root == null) return res.toList
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
var curNode: TreeNode = null
|
||||
for (i <- 0 until len) {
|
||||
curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
res.append(curNode.value) // 把最后一个节点的值加入解集
|
||||
}
|
||||
res.toList // 最后需要把res转换为List,return关键字可以省略
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 637.二叉树的层平均值
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/)
|
||||
@ -1060,6 +1136,30 @@ func averageOfLevels(_ root: TreeNode?) -> [Double] {
|
||||
return result
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
// 637.二叉树的层平均值
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def averageOfLevels(root: TreeNode): Array[Double] = {
|
||||
val res = mutable.ArrayBuffer[Double]()
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
var sum = 0.0
|
||||
var len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
var curNode = queue.dequeue()
|
||||
sum += curNode.value // 累加该层的值
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
res.append(sum / len) // 平均值即为sum/len
|
||||
}
|
||||
res.toArray // 最后需要转换为Array,return关键字可以省略
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 429.N叉树的层序遍历
|
||||
|
||||
@ -1304,6 +1404,34 @@ func levelOrder(_ root: Node?) -> [[Int]] {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 429.N叉树的层序遍历
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def levelOrder(root: Node): List[List[Int]] = {
|
||||
val res = mutable.ListBuffer[List[Int]]()
|
||||
if (root == null) return res.toList
|
||||
val queue = mutable.Queue[Node]()
|
||||
queue.enqueue(root) // 根节点入队
|
||||
while (!queue.isEmpty) {
|
||||
val tmp = mutable.ListBuffer[Int]() // 存储每层节点
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
tmp.append(curNode.value) // 将该节点的值加入tmp
|
||||
// 循环遍历该节点的子节点,加入队列
|
||||
for (child <- curNode.children) {
|
||||
queue.enqueue(child)
|
||||
}
|
||||
}
|
||||
res.append(tmp.toList) // 将该层的节点放到结果集
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 515.在每个树行中找最大值
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/)
|
||||
@ -1512,6 +1640,32 @@ func largestValues(_ root: TreeNode?) -> [Int] {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 515.在每个树行中找最大值
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def largestValues(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
if (root == null) return res.toList
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
var max = Int.MinValue // 初始化max为系统最小值
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
max = math.max(max, curNode.value) // 对比求解最大值
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
res.append(max) // 将最大值放入结果集
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 116.填充每个节点的下一个右侧节点指针
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/)
|
||||
@ -1771,6 +1925,35 @@ func connect(_ root: Node?) -> Node? {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 116.填充每个节点的下一个右侧节点指针
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
|
||||
def connect(root: Node): Node = {
|
||||
if (root == null) return root
|
||||
val queue = mutable.Queue[Node]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
val tmp = mutable.ListBuffer[Node]()
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
tmp.append(curNode)
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
// 处理next指针
|
||||
for (i <- 0 until tmp.size - 1) {
|
||||
tmp(i).next = tmp(i + 1)
|
||||
}
|
||||
tmp(tmp.size-1).next = null
|
||||
}
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
# 117.填充每个节点的下一个右侧节点指针II
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/)
|
||||
@ -2022,6 +2205,35 @@ func connect(_ root: Node?) -> Node? {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 117.填充每个节点的下一个右侧节点指针II
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
|
||||
def connect(root: Node): Node = {
|
||||
if (root == null) return root
|
||||
val queue = mutable.Queue[Node]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
val tmp = mutable.ListBuffer[Node]()
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
tmp.append(curNode)
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
// 处理next指针
|
||||
for (i <- 0 until tmp.size - 1) {
|
||||
tmp(i).next = tmp(i + 1)
|
||||
}
|
||||
tmp(tmp.size-1).next = null
|
||||
}
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
# 104.二叉树的最大深度
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)
|
||||
@ -2239,6 +2451,30 @@ func maxDepth(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 104.二叉树的最大深度
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def maxDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
var depth = 0
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.length
|
||||
depth += 1
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 111.二叉树的最小深度
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/)
|
||||
@ -2458,6 +2694,30 @@ func minDepth(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 111.二叉树的最小深度
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def minDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
depth += 1
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
if (curNode.left == null && curNode.right == null) return depth
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 总结
|
||||
|
||||
|
@ -495,7 +495,7 @@ class solution:
|
||||
|
||||
|
||||
## go
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
```go
|
||||
/**
|
||||
* definition for a binary tree node.
|
||||
@ -548,6 +548,8 @@ func maxdepth(root *treenode) int {
|
||||
|
||||
## javascript
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```javascript
|
||||
var maxdepth = function(root) {
|
||||
if (root === null) return 0;
|
||||
@ -595,6 +597,8 @@ var maxDepth = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
N叉树的最大深度 递归写法
|
||||
```js
|
||||
var maxDepth = function(root) {
|
||||
@ -627,9 +631,9 @@ var maxDepth = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
## TypeScript:
|
||||
## TypeScript
|
||||
|
||||
> 二叉树的最大深度:
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
@ -672,7 +676,7 @@ function maxDepth(root: TreeNode | null): number {
|
||||
};
|
||||
```
|
||||
|
||||
> N叉树的最大深度
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
@ -702,6 +706,8 @@ function maxDepth(root: TreeNode | null): number {
|
||||
|
||||
## C
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
二叉树最大深度递归
|
||||
```c
|
||||
int maxDepth(struct TreeNode* root){
|
||||
@ -758,7 +764,8 @@ int maxDepth(struct TreeNode* root){
|
||||
|
||||
## Swift
|
||||
|
||||
>二叉树最大深度
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```swift
|
||||
// 递归 - 后序
|
||||
func maxDepth1(_ root: TreeNode?) -> Int {
|
||||
@ -797,7 +804,8 @@ func maxDepth(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
>N叉树最大深度
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
```swift
|
||||
// 递归
|
||||
func maxDepth(_ root: Node?) -> Int {
|
||||
@ -833,5 +841,84 @@ func maxDepth1(_ root: Node?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def maxDepth(root: TreeNode): Int = {
|
||||
def process(curNode: TreeNode): Int = {
|
||||
if (curNode == null) return 0
|
||||
// 递归左节点和右节点,返回最大的,最后+1
|
||||
math.max(process(curNode.left), process(curNode.right)) + 1
|
||||
}
|
||||
// 调用递归方法,return关键字可以省略
|
||||
process(root)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def maxDepth(root: TreeNode): Int = {
|
||||
var depth = 0
|
||||
if (root == null) return depth
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
depth += 1 // 只要有层次就+=1
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def maxDepth(root: Node): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
for (node <- root.children) {
|
||||
depth = math.max(depth, maxDepth(node))
|
||||
}
|
||||
depth + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法: (层序遍历)
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def maxDepth(root: Node): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
val queue = mutable.Queue[Node]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
depth += 1
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
for (node <- curNode.children) queue.enqueue(node)
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -208,7 +208,7 @@ int getHeight(TreeNode* node) {
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
|
||||
int getHeight(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
|
@ -488,6 +488,46 @@ func minDepth(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Scala
|
||||
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def minDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
if (root.left == null && root.right != null) return 1 + minDepth(root.right)
|
||||
if (root.left != null && root.right == null) return 1 + minDepth(root.left)
|
||||
// 如果两侧都不为空,则取最小值,return关键字可以省略
|
||||
1 + math.min(minDepth(root.left), minDepth(root.right))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def minDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
depth += 1
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
if (curNode.left == null && curNode.right == null) return depth
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
@ -550,6 +590,7 @@ impl Solution {
|
||||
}
|
||||
min_depth
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-----------------------
|
||||
|
@ -267,6 +267,36 @@ const numDistinct = (s, t) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function numDistinct(s: string, t: string): number {
|
||||
/**
|
||||
dp[i][j]: s前i个字符,t前j个字符,s子序列中t出现的个数
|
||||
dp[0][0]=1, 表示s前0个字符为'',t前0个字符为''
|
||||
*/
|
||||
const sLen: number = s.length,
|
||||
tLen: number = t.length;
|
||||
const dp: number[][] = new Array(sLen + 1).fill(0)
|
||||
.map(_ => new Array(tLen + 1).fill(0));
|
||||
for (let m = 0; m < sLen; m++) {
|
||||
dp[m][0] = 1;
|
||||
}
|
||||
for (let i = 1; i <= sLen; i++) {
|
||||
for (let j = 1; j <= tLen; j++) {
|
||||
if (s[i - 1] === t[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[sLen][tLen];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -442,7 +442,7 @@ var partition = function(s) {
|
||||
}
|
||||
for(let j = i; j < len; j++) {
|
||||
if(!isPalindrome(s, i, j)) continue;
|
||||
path.push(s.substr(i, j - i + 1));
|
||||
path.push(s.slice(i, j + 1));
|
||||
backtracking(j + 1);
|
||||
path.pop();
|
||||
}
|
||||
|
@ -325,6 +325,33 @@ func evalRPN(_ tokens: [String]) -> Int {
|
||||
return stack.last!
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def evalRPN(tokens: Array[String]): Int = {
|
||||
val stack = mutable.Stack[Int]() // 定义栈
|
||||
// 抽取运算操作,需要传递x,y,和一个函数
|
||||
def operator(x: Int, y: Int, f: (Int, Int) => Int): Int = f(x, y)
|
||||
for (token <- tokens) {
|
||||
// 模式匹配,匹配不同的操作符做什么样的运算
|
||||
token match {
|
||||
// 最后一个参数 _+_,代表x+y,遵循Scala的函数至简原则,以下运算同理
|
||||
case "+" => stack.push(operator(stack.pop(), stack.pop(), _ + _))
|
||||
case "-" => stack.push(operator(stack.pop(), stack.pop(), -_ + _))
|
||||
case "*" => stack.push(operator(stack.pop(), stack.pop(), _ * _))
|
||||
case "/" => {
|
||||
var pop1 = stack.pop()
|
||||
var pop2 = stack.pop()
|
||||
stack.push(operator(pop2, pop1, _ / _))
|
||||
}
|
||||
case _ => stack.push(token.toInt) // 不是运算符就入栈
|
||||
}
|
||||
}
|
||||
// 最后返回栈顶,不需要加return关键字
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -817,6 +817,53 @@ object Solution {
|
||||
```
|
||||
|
||||
|
||||
PHP:
|
||||
```php
|
||||
function reverseWords($s) {
|
||||
$this->removeExtraSpaces($s);
|
||||
$this->reverseString($s, 0, strlen($s)-1);
|
||||
// 将每个单词反转
|
||||
$start = 0;
|
||||
for ($i = 0; $i <= strlen($s); $i++) {
|
||||
// 到达空格或者串尾,说明一个单词结束。进行翻转。
|
||||
if ($i == strlen($s) || $s[$i] == ' ') {
|
||||
// 翻转,注意是左闭右闭 []的翻转。
|
||||
$this->reverseString($s, $start, $i-1);
|
||||
// +1: 单词与单词直接有个空格
|
||||
$start = $i + 1;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
// 移除多余空格
|
||||
function removeExtraSpaces(&$s){
|
||||
$slow = 0;
|
||||
for ($i = 0; $i < strlen($s); $i++) {
|
||||
if ($s[$i] != ' ') {
|
||||
if ($slow != 0){
|
||||
$s[$slow++] = ' ';
|
||||
}
|
||||
while ($i < strlen($s) && $s[$i] != ' ') {
|
||||
$s[$slow++] = $s[$i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 移动覆盖处理,丢弃多余的脏数据。
|
||||
$s = substr($s,0,$slow);
|
||||
return ;
|
||||
}
|
||||
|
||||
// 翻转字符串
|
||||
function reverseString(&$s, $start, $end) {
|
||||
for ($i = $start, $j = $end; $i < $j; $i++, $j--) {
|
||||
$tmp = $s[$i];
|
||||
$s[$i] = $s[$j];
|
||||
$s[$j] = $tmp;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -417,6 +417,7 @@ object Solution {
|
||||
}
|
||||
sum
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
C#:
|
||||
|
@ -496,8 +496,26 @@ struct ListNode* reverseList(struct ListNode* head){
|
||||
return reverse(NULL, head);
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
|
||||
|
||||
|
||||
PHP:
|
||||
```php
|
||||
// 双指针法:
|
||||
function reverseList($head) {
|
||||
$cur = $head;
|
||||
$pre = NULL;
|
||||
while($cur){
|
||||
$temp = $cur->next;
|
||||
$cur->next = $pre;
|
||||
$pre = $cur;
|
||||
$cur = $temp;
|
||||
}
|
||||
return $pre;
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
双指针法:
|
||||
```scala
|
||||
object Solution {
|
||||
@ -529,6 +547,7 @@ object Solution {
|
||||
cur.next = pre
|
||||
reverse(cur, tmp) // 此时cur成为前一个节点,tmp是当前节点
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
|
@ -5,7 +5,7 @@
|
||||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
## 209.长度最小的子数组
|
||||
# 209.长度最小的子数组
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
输出:2
|
||||
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
|
||||
|
||||
# 思路
|
||||
|
||||
为了易于大家理解,我特意录制了[拿下滑动窗口! | LeetCode 209 长度最小的子数组](https://www.bilibili.com/video/BV1tZ4y1q7XE)
|
||||
|
||||
## 暴力解法
|
||||
|
||||
@ -47,8 +50,8 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
时间复杂度:O(n^2)
|
||||
空间复杂度:O(1)
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
## 滑动窗口
|
||||
|
||||
@ -56,6 +59,20 @@ public:
|
||||
|
||||
所谓滑动窗口,**就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果**。
|
||||
|
||||
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
|
||||
|
||||
那么滑动窗口如何用一个for循环来完成这个操作呢。
|
||||
|
||||
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
|
||||
|
||||
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
|
||||
|
||||
此时难免再次陷入 暴力解法的怪圈。
|
||||
|
||||
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
|
||||
|
||||
那么问题来了, 滑动窗口的起始位置如何移动呢?
|
||||
|
||||
这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
|
||||
|
||||

|
||||
@ -74,7 +91,7 @@ public:
|
||||
|
||||
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
|
||||
|
||||
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
|
||||
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
|
||||
|
||||
解题的关键在于 窗口的起始位置如何移动,如图所示:
|
||||
|
||||
@ -107,8 +124,8 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
时间复杂度:O(n)
|
||||
空间复杂度:O(1)
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
**一些录友会疑惑为什么时间复杂度是O(n)**。
|
||||
|
||||
|
@ -360,39 +360,30 @@ func backTree(n,k,startIndex int,track *[]int,result *[][]int){
|
||||
## javaScript
|
||||
|
||||
```js
|
||||
// 等差数列
|
||||
var maxV = k => k * (9 + 10 - k) / 2;
|
||||
var minV = k => k * (1 + k) / 2;
|
||||
/**
|
||||
* @param {number} k
|
||||
* @param {number} n
|
||||
* @return {number[][]}
|
||||
*/
|
||||
var combinationSum3 = function(k, n) {
|
||||
if (k > 9 || k < 1) return [];
|
||||
// if (n > maxV(k) || n < minV(k)) return [];
|
||||
// if (n === maxV(k)) return [Array.from({length: k}).map((v, i) => 9 - i)];
|
||||
// if (n === minV(k)) return [Array.from({length: k}).map((v, i) => i + 1)];
|
||||
|
||||
const res = [], path = [];
|
||||
backtracking(k, n, 1, 0);
|
||||
return res;
|
||||
function backtracking(k, n, i, sum){
|
||||
const len = path.length;
|
||||
if (len > k || sum > n) return;
|
||||
if (maxV(k - len) < n - sum) return;
|
||||
if (minV(k - len) > n - sum) return;
|
||||
|
||||
if(len === k && sum == n) {
|
||||
res.push(Array.from(path));
|
||||
const backtrack = (start) => {
|
||||
const l = path.length;
|
||||
if (l === k) {
|
||||
const sum = path.reduce((a, b) => a + b);
|
||||
if (sum === n) {
|
||||
res.push([...path]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const min = Math.min(n - sum, 9 + len - k + 1);
|
||||
|
||||
for(let a = i; a <= min; a++) {
|
||||
path.push(a);
|
||||
sum += a;
|
||||
backtracking(k, n, a + 1, sum);
|
||||
for (let i = start; i <= 9 - (k - l) + 1; i++) {
|
||||
path.push(i);
|
||||
backtrack(i + 1);
|
||||
path.pop();
|
||||
sum -= a;
|
||||
}
|
||||
}
|
||||
let res = [], path = [];
|
||||
backtrack(1);
|
||||
return res;
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -646,5 +646,68 @@ func countNodes(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if(root == null) return 0
|
||||
1 + countNodes(root.left) + countNodes(root.right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
层序遍历:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
var node = 0
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
node += 1
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
}
|
||||
node
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
利用完全二叉树性质:
|
||||
```scala
|
||||
object Solution {
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
var leftNode = root.left
|
||||
var rightNode = root.right
|
||||
// 向左向右往下探
|
||||
var leftDepth = 0
|
||||
while (leftNode != null) {
|
||||
leftDepth += 1
|
||||
leftNode = leftNode.left
|
||||
}
|
||||
var rightDepth = 0
|
||||
while (rightNode != null) {
|
||||
rightDepth += 1
|
||||
rightNode = rightNode.right
|
||||
}
|
||||
// 如果相等就是一个满二叉树
|
||||
if (leftDepth == rightDepth) {
|
||||
return (2 << leftDepth) - 1
|
||||
}
|
||||
// 如果不相等就不是一个完全二叉树,继续向下递归
|
||||
countNodes(root.left) + countNodes(root.right) + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -368,9 +368,7 @@ func invertTree(root *TreeNode) *TreeNode {
|
||||
if root ==nil{
|
||||
return nil
|
||||
}
|
||||
temp:=root.Left
|
||||
root.Left=root.Right
|
||||
root.Right=temp
|
||||
root.Left,root.Right=root.Right,root.Left//交换
|
||||
|
||||
invertTree(root.Left)
|
||||
invertTree(root.Right)
|
||||
@ -820,5 +818,53 @@ func invertTree(_ root: TreeNode?) -> TreeNode? {
|
||||
}
|
||||
```
|
||||
|
||||
### Scala
|
||||
|
||||
深度优先遍历(前序遍历):
|
||||
```scala
|
||||
object Solution {
|
||||
def invertTree(root: TreeNode): TreeNode = {
|
||||
if (root == null) return root
|
||||
// 递归
|
||||
def process(node: TreeNode): Unit = {
|
||||
if (node == null) return
|
||||
// 翻转节点
|
||||
val curNode = node.left
|
||||
node.left = node.right
|
||||
node.right = curNode
|
||||
process(node.left)
|
||||
process(node.right)
|
||||
}
|
||||
process(root)
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
广度优先遍历(层序遍历):
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def invertTree(root: TreeNode): TreeNode = {
|
||||
if (root == null) return root
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
var curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
// 翻转
|
||||
var tmpNode = curNode.left
|
||||
curNode.left = curNode.right
|
||||
curNode.right = tmpNode
|
||||
}
|
||||
}
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -630,6 +630,53 @@ func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
|
||||
return result
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
object Solution {
|
||||
def maxSlidingWindow(nums: Array[Int], k: Int): Array[Int] = {
|
||||
var len = nums.length - k + 1 // 滑动窗口长度
|
||||
var res: Array[Int] = new Array[Int](len) // 声明存储结果的数组
|
||||
var index = 0 // 结果数组指针
|
||||
val queue: MyQueue = new MyQueue // 自定义队列
|
||||
// 将前k个添加到queue
|
||||
for (i <- 0 until k) {
|
||||
queue.add(nums(i))
|
||||
}
|
||||
res(index) = queue.peek // 第一个滑动窗口的最大值
|
||||
index += 1
|
||||
for (i <- k until nums.length) {
|
||||
queue.poll(nums(i - k)) // 首先移除第i-k个元素
|
||||
queue.add(nums(i)) // 添加当前数字到队列
|
||||
res(index) = queue.peek() // 赋值
|
||||
index+=1
|
||||
}
|
||||
// 最终返回res,return关键字可以省略
|
||||
res
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MyQueue {
|
||||
var queue = ArrayBuffer[Int]()
|
||||
|
||||
// 移除元素,如果传递进来的跟队头相等,那么移除
|
||||
def poll(value: Int): Unit = {
|
||||
if (!queue.isEmpty && queue.head == value) {
|
||||
queue.remove(0)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加元素,当队尾大于当前元素就删除
|
||||
def add(value: Int): Unit = {
|
||||
while (!queue.isEmpty && value > queue.last) {
|
||||
queue.remove(queue.length - 1)
|
||||
}
|
||||
queue.append(value)
|
||||
}
|
||||
|
||||
def peek(): Int = queue.head
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -702,5 +702,35 @@ func binaryTreePaths(_ root: TreeNode?) -> [String] {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def binaryTreePaths(root: TreeNode): List[String] = {
|
||||
val res = ListBuffer[String]()
|
||||
def traversal(curNode: TreeNode, path: ListBuffer[Int]): Unit = {
|
||||
path.append(curNode.value)
|
||||
if (curNode.left == null && curNode.right == null) {
|
||||
res.append(path.mkString("->")) // mkString函数: 将数组的所有值按照指定字符串拼接
|
||||
return // 处理完可以直接return
|
||||
}
|
||||
|
||||
if (curNode.left != null) {
|
||||
traversal(curNode.left, path)
|
||||
path.remove(path.size - 1)
|
||||
}
|
||||
if (curNode.right != null) {
|
||||
traversal(curNode.right, path)
|
||||
path.remove(path.size - 1)
|
||||
}
|
||||
}
|
||||
traversal(root, ListBuffer[Int]())
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -237,6 +237,27 @@ const lengthOfLIS = (nums) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript
|
||||
|
||||
```typescript
|
||||
function lengthOfLIS(nums: number[]): number {
|
||||
/**
|
||||
dp[i]: 前i个元素中,以nums[i]结尾,最长子序列的长度
|
||||
*/
|
||||
const dp: number[] = new Array(nums.length).fill(1);
|
||||
let resMax: number = 0;
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (nums[i] > nums[j]) {
|
||||
dp[i] = Math.max(dp[i], dp[j] + 1);
|
||||
}
|
||||
}
|
||||
resMax = Math.max(resMax, dp[i]);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -266,6 +266,38 @@ public class Solution
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
PHP:
|
||||
```php
|
||||
// 双指针
|
||||
// 一:
|
||||
function reverseString(&$s) {
|
||||
$left = 0;
|
||||
$right = count($s)-1;
|
||||
while($left<$right){
|
||||
$temp = $s[$left];
|
||||
$s[$left] = $s[$right];
|
||||
$s[$right] = $temp;
|
||||
$left++;
|
||||
$right--;
|
||||
}
|
||||
}
|
||||
|
||||
// 二:
|
||||
function reverseString(&$s) {
|
||||
$this->reverse($s,0,count($s)-1);
|
||||
}
|
||||
// 按指定位置交换元素
|
||||
function reverse(&$s, $start, $end) {
|
||||
for ($i = $start, $j = $end; $i < $j; $i++, $j--) {
|
||||
$tmp = $s[$i];
|
||||
$s[$i] = $s[$j];
|
||||
$s[$j] = $tmp;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
|
@ -374,7 +374,49 @@ function topKFrequent(nums: number[], k: number): number[] {
|
||||
};
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
解法一: 优先级队列
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def topKFrequent(nums: Array[Int], k: Int): Array[Int] = {
|
||||
val map = mutable.HashMap[Int, Int]()
|
||||
// 将所有元素都放入Map
|
||||
for (num <- nums) {
|
||||
map.put(num, map.getOrElse(num, 0) + 1)
|
||||
}
|
||||
// 声明一个优先级队列,在函数柯里化那块需要指明排序方式
|
||||
var queue = mutable.PriorityQueue[(Int, Int)]()(Ordering.fromLessThan((x, y) => x._2 > y._2))
|
||||
// 将map里面的元素送入优先级队列
|
||||
for (elem <- map) {
|
||||
queue.enqueue(elem)
|
||||
if(queue.size > k){
|
||||
queue.dequeue // 如果队列元素大于k个,出队
|
||||
}
|
||||
}
|
||||
// 最终只需要key的Array形式就可以了,return关键字可以省略
|
||||
queue.map(_._1).toArray
|
||||
}
|
||||
}
|
||||
```
|
||||
解法二: 相当于一个wordCount程序
|
||||
```scala
|
||||
object Solution {
|
||||
def topKFrequent(nums: Array[Int], k: Int): Array[Int] = {
|
||||
// 首先将数据变为(x,1),然后按照x分组,再使用map进行转换(x,sum),变换为Array
|
||||
// 再使用sort针对sum进行排序,最后take前k个,再把数据变为x,y,z这种格式
|
||||
nums.map((_, 1)).groupBy(_._1)
|
||||
.map {
|
||||
case (x, arr) => (x, arr.map(_._2).sum)
|
||||
}
|
||||
.toArray
|
||||
.sortWith(_._2 > _._2)
|
||||
.take(k)
|
||||
.map(_._1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -356,6 +356,8 @@ object Solution {
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
|
@ -425,6 +425,7 @@ object Solution {
|
||||
true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
C#:
|
||||
|
@ -201,7 +201,32 @@ const isSubsequence = (s, t) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function isSubsequence(s: string, t: string): boolean {
|
||||
/**
|
||||
dp[i][j]: s的前i-1个,t的前j-1个,最长公共子序列的长度
|
||||
*/
|
||||
const sLen: number = s.length,
|
||||
tLen: number = t.length;
|
||||
const dp: number[][] = new Array(sLen + 1).fill(0)
|
||||
.map(_ => new Array(tLen + 1).fill(0));
|
||||
for (let i = 1; i <= sLen; i++) {
|
||||
for (let j = 1; j <= tLen; j++) {
|
||||
if (s[i - 1] === t[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 dp[sLen][tLen] === s.length;
|
||||
};
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func isSubsequence(s string, t string) bool {
|
||||
dp := make([][]int,len(s)+1)
|
||||
|
@ -516,6 +516,44 @@ int sumOfLeftLeaves(struct TreeNode* root){
|
||||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
**递归:**
|
||||
```scala
|
||||
object Solution {
|
||||
def sumOfLeftLeaves(root: TreeNode): Int = {
|
||||
if(root == null) return 0
|
||||
var midValue = 0
|
||||
if(root.left != null && root.left.left == null && root.left.right == null){
|
||||
midValue = root.left.value
|
||||
}
|
||||
// return关键字可以省略
|
||||
midValue + sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**迭代:**
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def sumOfLeftLeaves(root: TreeNode): Int = {
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
if (root == null) return 0
|
||||
stack.push(root)
|
||||
var sum = 0
|
||||
while (!stack.isEmpty) {
|
||||
val curNode = stack.pop()
|
||||
if (curNode.left != null && curNode.left.left == null && curNode.left.right == null) {
|
||||
sum += curNode.left.value // 如果满足条件就累加
|
||||
}
|
||||
if (curNode.right != null) stack.push(curNode.right)
|
||||
if (curNode.left != null) stack.push(curNode.left)
|
||||
}
|
||||
sum
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -417,6 +417,26 @@ var canPartition = function(nums) {
|
||||
```
|
||||
|
||||
|
||||
TypeScript:
|
||||
|
||||
```ts
|
||||
function canPartition(nums: number[]): boolean {
|
||||
const sum: number = nums.reduce((a: number, b: number): number => a + b);
|
||||
if (sum % 2 === 1) return false;
|
||||
const target: number = sum / 2;
|
||||
// dp[j]表示容量(总数和)为j的背包所能装下的数(下标[0, i]之间任意取)的总和(<= 容量)的最大值
|
||||
const dp: number[] = new Array(target + 1).fill(0);
|
||||
const n: number = nums.length;
|
||||
for (let i: number = 0; i < n; i++) {
|
||||
for (let j: number = target; j >= nums[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
|
||||
}
|
||||
}
|
||||
return dp[target] === target;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
C:
|
||||
二维dp:
|
||||
```c
|
||||
@ -575,6 +595,5 @@ function canPartition(nums: number[]): boolean {
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -354,6 +354,7 @@ object Solution {
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
|
@ -351,22 +351,26 @@ const findTargetSumWays = (nums, target) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
TypeScript:
|
||||
|
||||
```ts
|
||||
function findTargetSumWays(nums: number[], target: number): number {
|
||||
const sum: number = nums.reduce((pre, cur) => pre + cur);
|
||||
if (Math.abs(target) > sum) return 0;
|
||||
if ((target + sum) % 2 === 1) return 0;
|
||||
const bagSize: number = (target + sum) / 2;
|
||||
const dp: number[] = new Array(bagSize + 1).fill(0);
|
||||
dp[0] = 1;
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
for (let j = bagSize; j >= nums[i]; j--) {
|
||||
// 把数组分成两个组合left, right.left + right = sum, left - right = target.
|
||||
const sum: number = nums.reduce((a: number, b: number): number => a + b);
|
||||
if ((sum + target) % 2 || Math.abs(target) > sum) return 0;
|
||||
const left: number = (sum + target) / 2;
|
||||
|
||||
// 将问题转化为装满容量为left的背包有多少种方法
|
||||
// dp[i]表示装满容量为i的背包有多少种方法
|
||||
const dp: number[] = new Array(left + 1).fill(0);
|
||||
dp[0] = 1; // 装满容量为0的背包有1种方法(什么也不装)
|
||||
for (let i: number = 0; i < nums.length; i++) {
|
||||
for (let j: number = left; j >= nums[i]; j--) {
|
||||
dp[j] += dp[j - nums[i]];
|
||||
}
|
||||
}
|
||||
return dp[bagSize];
|
||||
return dp[left];
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -332,5 +332,36 @@ var nextGreaterElement = function (nums1, nums2) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function nextGreaterElement(nums1: number[], nums2: number[]): number[] {
|
||||
const resArr: number[] = new Array(nums1.length).fill(-1);
|
||||
const stack: number[] = [];
|
||||
const helperMap: Map<number, number> = new Map();
|
||||
nums1.forEach((num, index) => {
|
||||
helperMap.set(num, index);
|
||||
})
|
||||
stack.push(0);
|
||||
for (let i = 1, length = nums2.length; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
while (stack.length > 0 && nums2[top] < nums2[i]) {
|
||||
let index = helperMap.get(nums2[top]);
|
||||
if (index !== undefined) {
|
||||
resArr[index] = nums2[i];
|
||||
}
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
if (helperMap.get(nums2[i]) !== undefined) {
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -182,5 +182,31 @@ var nextGreaterElements = function (nums) {
|
||||
return res;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function nextGreaterElements(nums: number[]): number[] {
|
||||
const length: number = nums.length;
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
const resArr: number[] = new Array(length).fill(-1);
|
||||
for (let i = 1; i < length * 2; i++) {
|
||||
const index = i % length;
|
||||
let top = stack[stack.length - 1];
|
||||
while (stack.length > 0 && nums[top] < nums[index]) {
|
||||
resArr[top] = nums[index];
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
if (i < length) {
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -546,7 +546,50 @@ func findBottomLeftValue(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
递归版本:
|
||||
```scala
|
||||
object Solution {
|
||||
def findBottomLeftValue(root: TreeNode): Int = {
|
||||
var maxLeftValue = 0
|
||||
var maxLen = Int.MinValue
|
||||
// 递归方法
|
||||
def traversal(node: TreeNode, leftLen: Int): Unit = {
|
||||
// 如果左右都为空并且当前深度大于最大深度,记录最左节点的值
|
||||
if (node.left == null && node.right == null && leftLen > maxLen) {
|
||||
maxLen = leftLen
|
||||
maxLeftValue = node.value
|
||||
}
|
||||
if (node.left != null) traversal(node.left, leftLen + 1)
|
||||
if (node.right != null) traversal(node.right, leftLen + 1)
|
||||
}
|
||||
traversal(root, 0) // 调用方法
|
||||
maxLeftValue // return关键字可以省略
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
层序遍历:
|
||||
```scala
|
||||
import scala.collection.mutable
|
||||
|
||||
def findBottomLeftValue(root: TreeNode): Int = {
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
var res = 0 // 记录每层最左侧结果
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (i == 0) res = curNode.value // 记录最最左侧的节点
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
}
|
||||
res // 最终返回结果,return关键字可以省略
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -235,6 +235,35 @@ const longestPalindromeSubseq = (s) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function longestPalindromeSubseq(s: string): number {
|
||||
/**
|
||||
dp[i][j]:[i,j]区间内,最长回文子序列的长度
|
||||
*/
|
||||
const length: number = s.length;
|
||||
const dp: number[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(length).fill(0));
|
||||
for (let i = 0; i < length; i++) {
|
||||
dp[i][i] = 1;
|
||||
}
|
||||
// 自下而上,自左往右遍历
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
for (let j = i + 1; j < length; j++) {
|
||||
if (s[i] === s[j]) {
|
||||
dp[i][j] = dp[i + 1][j - 1] + 2;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[0][length - 1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -229,6 +229,67 @@ const minDistance = (word1, word2) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> dp版本一:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,所需最小步数
|
||||
dp[0][0]=0: word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 0; i <= length1; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let i = 0; i <= length2; i++) {
|
||||
dp[0][i] = i;
|
||||
}
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
> dp版本二:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,最长公共子序列的长度
|
||||
dp[0][0]=0: word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const maxLen: number = dp[length1][length2];
|
||||
return length1 + length2 - maxLen * 2;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -406,6 +406,63 @@ const countSubstrings = (s) => {
|
||||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划:
|
||||
|
||||
```typescript
|
||||
function countSubstrings(s: string): number {
|
||||
/**
|
||||
dp[i][j]: [i,j]区间内的字符串是否为回文(左闭右闭)
|
||||
*/
|
||||
const length: number = s.length;
|
||||
const dp: boolean[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(length).fill(false));
|
||||
let resCount: number = 0;
|
||||
// 自下而上,自左向右遍历
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
for (let j = i; j < length; j++) {
|
||||
if (
|
||||
s[i] === s[j] &&
|
||||
(j - i <= 1 || dp[i + 1][j - 1] === true)
|
||||
) {
|
||||
dp[i][j] = true;
|
||||
resCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
```
|
||||
|
||||
> 双指针法:
|
||||
|
||||
```typescript
|
||||
function countSubstrings(s: string): number {
|
||||
const length: number = s.length;
|
||||
let resCount: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
resCount += expandRange(s, i, i);
|
||||
resCount += expandRange(s, i, i + 1);
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
function expandRange(s: string, left: number, right: number): number {
|
||||
let palindromeNum: number = 0;
|
||||
while (
|
||||
left >= 0 && right < s.length &&
|
||||
s[left] === s[right]
|
||||
) {
|
||||
palindromeNum++;
|
||||
left--;
|
||||
right++;
|
||||
}
|
||||
return palindromeNum;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -338,6 +338,45 @@ const findLengthOfLCIS = (nums) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划:
|
||||
|
||||
```typescript
|
||||
function findLengthOfLCIS(nums: number[]): number {
|
||||
/**
|
||||
dp[i]: 前i个元素,以nums[i]结尾,最长连续子序列的长度
|
||||
*/
|
||||
const dp: number[] = new Array(nums.length).fill(1);
|
||||
let resMax: number = 1;
|
||||
for (let i = 1, length = nums.length; i < length; i++) {
|
||||
if (nums[i] > nums[i - 1]) {
|
||||
dp[i] = dp[i - 1] + 1;
|
||||
}
|
||||
resMax = Math.max(resMax, dp[i]);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 贪心:
|
||||
|
||||
```typescript
|
||||
function findLengthOfLCIS(nums: number[]): number {
|
||||
let resMax: number = 1;
|
||||
let count: number = 1;
|
||||
for (let i = 0, length = nums.length; i < length - 1; i++) {
|
||||
if (nums[i] < nums[i + 1]) {
|
||||
count++;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
resMax = Math.max(resMax, count);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -36,6 +36,8 @@
|
||||
|
||||
## 思路
|
||||
|
||||
为了易于大家理解,我还录制了视频,可以看这里:[手把手带你撕出正确的二分法](https://www.bilibili.com/video/BV1fA4y1o715)
|
||||
|
||||
**这道题目的前提是数组为有序数组**,同时题目还强调**数组中无重复元素**,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
|
||||
|
||||
二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 `while(left < right)` 还是 `while(left <= right)`,到底是`right = middle`呢,还是要`right = middle - 1`呢?
|
||||
@ -610,6 +612,40 @@ public class Solution{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**Kotlin:**
|
||||
```Kotlin
|
||||
// (版本一)左闭右开区间
|
||||
class Solution {
|
||||
fun search(nums: IntArray, target: Int): Int {
|
||||
var left = 0
|
||||
var right = nums.size // [left,right) 右侧为开区间,right 设置为 nums.size
|
||||
while (left < right) {
|
||||
val mid = (left + right) / 2
|
||||
if (nums[mid] < target) left = mid + 1
|
||||
else if (nums[mid] > target) right = mid // 代码的核心,循环中 right 是开区间,这里也应是开区间
|
||||
else return mid
|
||||
}
|
||||
return -1 // 没有找到 target ,返回 -1
|
||||
}
|
||||
}
|
||||
// (版本二)左闭右闭区间
|
||||
class Solution {
|
||||
fun search(nums: IntArray, target: Int): Int {
|
||||
var left = 0
|
||||
var right = nums.size - 1 // [left,right] 右侧为闭区间,right 设置为 nums.size - 1
|
||||
while (left <= right) {
|
||||
val mid = (left + right) / 2
|
||||
if (nums[mid] < target) left = mid + 1
|
||||
else if (nums[mid] > target) right = mid - 1 // 代码的核心,循环中 right 是闭区间,这里也应是闭区间
|
||||
else return mid
|
||||
}
|
||||
return -1 // 没有找到 target ,返回 -1
|
||||
}
|
||||
}
|
||||
```
|
||||
**Scala:**
|
||||
|
||||
(版本一)左闭右闭区间
|
||||
@ -653,5 +689,6 @@ object Solution {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -297,6 +297,56 @@ const findLength = (nums1, nums2) => {
|
||||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划:
|
||||
|
||||
```typescript
|
||||
function findLength(nums1: number[], nums2: number[]): number {
|
||||
/**
|
||||
dp[i][j]:nums[i-1]和nums[j-1]结尾,最长重复子数组的长度
|
||||
*/
|
||||
const length1: number = nums1.length,
|
||||
length2: number = nums2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (nums1[i - 1] === nums2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
resMax = Math.max(resMax, dp[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 滚动数组:
|
||||
|
||||
```typescript
|
||||
function findLength(nums1: number[], nums2: number[]): number {
|
||||
const length1: number = nums1.length,
|
||||
length2: number = nums2.length;
|
||||
const dp: number[] = new Array(length1 + 1).fill(0);
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = length2; j >= 1; j--) {
|
||||
if (nums1[i - 1] === nums2[j - 1]) {
|
||||
dp[j] = dp[j - 1] + 1;
|
||||
resMax = Math.max(resMax, dp[j]);
|
||||
} else {
|
||||
dp[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -372,6 +372,32 @@ var dailyTemperatures = function(temperatures) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 精简版:
|
||||
|
||||
```typescript
|
||||
function dailyTemperatures(temperatures: number[]): number[] {
|
||||
const length: number = temperatures.length;
|
||||
const stack: number[] = [];
|
||||
const resArr: number[] = new Array(length).fill(0);
|
||||
stack.push(0);
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
while (
|
||||
stack.length > 0 &&
|
||||
temperatures[top] < temperatures[i]
|
||||
) {
|
||||
resArr[top] = i - top;
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -158,6 +158,71 @@ class Solution {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
class Solution{
|
||||
/*解法二: 上述c++补充思路的Java代码实现*/
|
||||
|
||||
public int[][] findPartitions(String s) {
|
||||
List<Integer> temp = new ArrayList<>();
|
||||
int[][] hash = new int[26][2];//26个字母2列 表示该字母对应的区间
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
//更新字符c对应的位置i
|
||||
char c = s.charAt(i);
|
||||
if (hash[c - 'a'][0] == 0) hash[c - 'a'][0] = i;
|
||||
|
||||
hash[c - 'a'][1] = i;
|
||||
|
||||
//第一个元素区别对待一下
|
||||
hash[s.charAt(0) - 'a'][0] = 0;
|
||||
}
|
||||
|
||||
|
||||
List<List<Integer>> h = new LinkedList<>();
|
||||
//组装区间
|
||||
for (int i = 0; i < 26; i++) {
|
||||
//if (hash[i][0] != hash[i][1]) {
|
||||
temp.clear();
|
||||
temp.add(hash[i][0]);
|
||||
temp.add(hash[i][1]);
|
||||
//System.out.println(temp);
|
||||
h.add(new ArrayList<>(temp));
|
||||
// }
|
||||
}
|
||||
// System.out.println(h);
|
||||
// System.out.println(h.size());
|
||||
int[][] res = new int[h.size()][2];
|
||||
for (int i = 0; i < h.size(); i++) {
|
||||
List<Integer> list = h.get(i);
|
||||
res[i][0] = list.get(0);
|
||||
res[i][1] = list.get(1);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
public List<Integer> partitionLabels(String s) {
|
||||
int[][] partitions = findPartitions(s);
|
||||
List<Integer> res = new ArrayList<>();
|
||||
Arrays.sort(partitions, (o1, o2) -> Integer.compare(o1[0], o2[0]));
|
||||
int right = partitions[0][1];
|
||||
int left = 0;
|
||||
for (int i = 0; i < partitions.length; i++) {
|
||||
if (partitions[i][0] > right) {
|
||||
//左边界大于右边界即可纪委一次分割
|
||||
res.add(right - left + 1);
|
||||
left = partitions[i][0];
|
||||
}
|
||||
right = Math.max(right, partitions[i][1]);
|
||||
|
||||
}
|
||||
//最右端
|
||||
res.add(right - left + 1);
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
@ -157,6 +157,26 @@ var validMountainArray = function(arr) {
|
||||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function validMountainArray(arr: number[]): boolean {
|
||||
const length: number = arr.length;
|
||||
if (length < 3) return false;
|
||||
let left: number = 0,
|
||||
right: number = length - 1;
|
||||
while (left < (length - 1) && arr[left] < arr[left + 1]) {
|
||||
left++;
|
||||
}
|
||||
while (right > 0 && arr[right] < arr[right - 1]) {
|
||||
right--;
|
||||
}
|
||||
if (left === right && left !== 0 && right !== length - 1)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
# 思路
|
||||
|
||||
为了易于大家理解,我还特意录制了视频,[本题视频讲解](https://www.bilibili.com/video/BV1QB4y1D7ep)
|
||||
|
||||
## 暴力排序
|
||||
|
||||
最直观的想法,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
|
||||
@ -358,6 +360,30 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
class Solution {
|
||||
// 双指针法
|
||||
fun sortedSquares(nums: IntArray): IntArray {
|
||||
var res = IntArray(nums.size)
|
||||
var left = 0 // 指向数组的最左端
|
||||
var right = nums.size - 1 // 指向数组端最右端
|
||||
// 选择平方数更大的那一个往 res 数组中倒序填充
|
||||
for (index in nums.size - 1 downTo 0) {
|
||||
if (nums[left] * nums[left] > nums[right] * nums[right]) {
|
||||
res[index] = nums[left] * nums[left]
|
||||
left++
|
||||
} else {
|
||||
res[index] = nums[right] * nums[right]
|
||||
right--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
双指针:
|
||||
|
@ -183,6 +183,30 @@ const maxUncrossedLines = (nums1, nums2) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxUncrossedLines(nums1: number[], nums2: number[]): number {
|
||||
/**
|
||||
dp[i][j]: nums1前i-1个,nums2前j-1个,最大连线数
|
||||
*/
|
||||
const length1: number = nums1.length,
|
||||
length2: number = nums2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (nums1[i - 1] === nums2[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 dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -277,26 +277,29 @@ var lastStoneWeightII = function (stones) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
TypeScript版本
|
||||
|
||||
```ts
|
||||
function lastStoneWeightII(stones: number[]): number {
|
||||
const sum: number = stones.reduce((pre, cur) => pre + cur);
|
||||
const bagSize: number = Math.floor(sum / 2);
|
||||
const weightArr: number[] = stones;
|
||||
const valueArr: number[] = stones;
|
||||
const goodsNum: number = weightArr.length;
|
||||
const dp: number[] = new Array(bagSize + 1).fill(0);
|
||||
for (let i = 0; i < goodsNum; i++) {
|
||||
for (let j = bagSize; j >= weightArr[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - weightArr[i]] + valueArr[i]);
|
||||
const sum: number = stones.reduce((a: number, b:number): number => a + b);
|
||||
const target: number = Math.floor(sum / 2);
|
||||
const n: number = stones.length;
|
||||
// dp[j]表示容量(总数和)为j的背包所能装下的数(下标[0, i]之间任意取)的总和(<= 容量)的最大值
|
||||
const dp: number[] = new Array(target + 1).fill(0);
|
||||
for (let i: number = 0; i < n; i++ ) {
|
||||
for (let j: number = target; j >= stones[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
|
||||
}
|
||||
}
|
||||
return sum - dp[bagSize] * 2;
|
||||
return sum - dp[target] - dp[target];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -236,6 +236,32 @@ const longestCommonSubsequence = (text1, text2) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function longestCommonSubsequence(text1: string, text2: string): number {
|
||||
/**
|
||||
dp[i][j]: text1中前i-1个和text2中前j-1个,最长公共子序列的长度
|
||||
*/
|
||||
const length1: number = text1.length,
|
||||
length2: number = text2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (text1[i - 1] === text2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -217,6 +217,46 @@ var smallerNumbersThanCurrent = function(nums) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 暴力法:
|
||||
|
||||
```typescript
|
||||
function smallerNumbersThanCurrent(nums: number[]): number[] {
|
||||
const length: number = nums.length;
|
||||
const resArr: number[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
let count: number = 0;
|
||||
for (let j = 0; j < length; j++) {
|
||||
if (nums[j] < nums[i]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
resArr[i] = count;
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
> 排序+hash
|
||||
|
||||
```typescript
|
||||
function smallerNumbersThanCurrent(nums: number[]): number[] {
|
||||
const length: number = nums.length;
|
||||
const sortedArr: number[] = [...nums];
|
||||
sortedArr.sort((a, b) => a - b);
|
||||
const hashMap: Map<number, number> = new Map();
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
hashMap.set(sortedArr[i], i);
|
||||
}
|
||||
const resArr: number[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
resArr[i] = hashMap.get(nums[i]);
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
312
problems/qita/gitserver.md
Normal file
312
problems/qita/gitserver.md
Normal file
@ -0,0 +1,312 @@
|
||||
|
||||
# 一文手把手教你搭建Git私服
|
||||
|
||||
## 为什么要搭建Git私服
|
||||
|
||||
很多同学都问文章,文档,资料怎么备份啊,自己电脑和公司电脑怎么随时同步资料啊等等,这里呢我写一个搭建自己的git私服的详细教程
|
||||
|
||||
为什么要搭建一个Git私服呢,而不是用Github免费的私有仓库,有以下几点:
|
||||
* Github 私有仓库真的慢,文件一旦多了,或者有图片文件,git pull 的时候半天拉不下来
|
||||
* 自己的文档难免有自己个人信息,放在github心里也是担心的
|
||||
* 想建几个库就建几个,想几个人合作开发都可以,不香么?
|
||||
|
||||
**网上可以搜到很多git搭建,但是说的模棱两可**,而且有的直接是在本地搭建git服务,既然是备份,搭建在本地哪有备份的意义,一定要有一个远端服务器, 而且自己的电脑和公司的电脑还是同步自己的文章,文档和资料等等。
|
||||
|
||||
|
||||
适合人群: 想通过git私服来备份自己的文章,Markdown,并做版本管理的同学
|
||||
最后,写好每篇 Chat 是对我的责任,也是对你的尊重。谢谢大家~
|
||||
|
||||
正文如下:
|
||||
|
||||
-----------------------------
|
||||
|
||||
## 如何找到可以外网访问服务器
|
||||
|
||||
有的同学问了,自己的电脑就不能作为服务器么?
|
||||
|
||||
这里要说一下,安装家庭带宽,运营商默认是不会给我们独立分配公网IP的
|
||||
|
||||
一般情况下是一片区域公用一个公网IP池,所以外网是不能访问到在家里我们使用的电脑的
|
||||
|
||||
除非我们自己去做映射,这其实非常麻烦而且公网IP池 是不断变化的
|
||||
|
||||
辛辛苦苦做了映射,运营商给IP一换,我们的努力就白扯了
|
||||
|
||||
那我们如何才能找到一个外网可以访问的服务器呢,此时云计算拯救了我们。
|
||||
|
||||
推荐大家选一家云厂商(阿里云,腾讯云,百度云都可以)在上面上买一台云服务器
|
||||
|
||||
* [阿里云活动期间服务器购买](https://www.aliyun.com/minisite/goods?taskCode=shareNew2205&recordId=3641992&userCode=roof0wob)
|
||||
* [腾讯云活动期间服务器购买](https://curl.qcloud.com/EiaMXllu)
|
||||
|
||||
云厂商经常做活动,如果从来没有买过云服务器的账号更便宜,低配一年一百块左右的样子,强烈推荐一起买个三年。
|
||||
|
||||
买云服务器的时候推荐直接安装centos系统。
|
||||
|
||||
这里要说一下,有了自己的云服务器之后 不仅仅可以用来做git私服
|
||||
|
||||
**同时还可以做网站,做程序后台,跑程序,做测试**(这样我们自己的电脑就不会因为自己各种搭建环境下载各种包而搞的的烂糟糟),等等等。
|
||||
|
||||
有自己云服务器和一个公网IP真的是一件非常非常幸福的事情,能体验到自己的服务随时可以部署上去提供给所有人使用的喜悦。
|
||||
|
||||
外网可以访问的服务器解决了,接下来就要部署git服务了
|
||||
|
||||
本文将采用centos系统来部署git私服
|
||||
|
||||
## 服务器端安装Git
|
||||
|
||||
切换至root账户
|
||||
|
||||
```
|
||||
su root
|
||||
```
|
||||
|
||||
看一下服务器有没有安装git,如果出现下面信息就说明是有git的
|
||||
```
|
||||
[root@instance-5fcyjde7 ~]# git
|
||||
usage: git [--version] [--help] [-c name=value]
|
||||
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
|
||||
[-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
|
||||
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
|
||||
<command> [<args>]
|
||||
|
||||
The most commonly used git commands are:
|
||||
add Add file contents to the index
|
||||
bisect Find by binary search the change that introduced a bug
|
||||
branch List, create, or delete branches
|
||||
checkout Checkout a branch or paths to the working tree
|
||||
clone Clone a repository into a new directory
|
||||
commit Record changes to the repository
|
||||
diff Show changes between commits, commit and working tree, etc
|
||||
fetch Download objects and refs from another repository
|
||||
grep Print lines matching a pattern
|
||||
init Create an empty Git repository or reinitialize an existing one
|
||||
log Show commit logs
|
||||
merge Join two or more development histories together
|
||||
mv Move or rename a file, a directory, or a symlink
|
||||
pull Fetch from and merge with another repository or a local branch
|
||||
push Update remote refs along with associated objects
|
||||
rebase Forward-port local commits to the updated upstream head
|
||||
reset Reset current HEAD to the specified state
|
||||
rm Remove files from the working tree and from the index
|
||||
show Show various types of objects
|
||||
status Show the working tree status
|
||||
tag Create, list, delete or verify a tag object signed with GPG
|
||||
|
||||
'git help -a' and 'git help -g' lists available subcommands and some
|
||||
concept guides. See 'git help <command>' or 'git help <concept>'
|
||||
to read about a specific subcommand or concept.
|
||||
```
|
||||
|
||||
如果没有git,就安装一下,yum安装的版本默认是 `1.8.3.1`
|
||||
|
||||
```
|
||||
yum install git
|
||||
```
|
||||
|
||||
安装成功之后,看一下自己安装的版本
|
||||
|
||||
```
|
||||
git --version
|
||||
```
|
||||
|
||||
## 服务器端设置Git账户
|
||||
|
||||
创建一个git的linux账户,这个账户只做git私服的操作,也是为了安全起见
|
||||
|
||||
如果不新创建一个linux账户,在自己的常用的linux账户下创建的话,哪天手抖 来一个`rm -rf *` 操作 数据可全没了
|
||||
|
||||
**这里linux git账户的密码设置的尽量复杂一些,我这里为了演示,就设置成为'gitpassword'**
|
||||
```
|
||||
adduser git
|
||||
passwd gitpassword
|
||||
```
|
||||
|
||||
然后就要切换成git账户,进行后面的操作了
|
||||
```
|
||||
[root@instance-5fcyjde7 ~]# su - git
|
||||
```
|
||||
|
||||
看一下自己所在的目录,是不是在git目录下面
|
||||
|
||||
```
|
||||
[git@instance-5fcyjde7 ~]$ pwd
|
||||
/home/git
|
||||
```
|
||||
|
||||
## 服务器端密钥管理
|
||||
|
||||
创建`.ssh` 目录,如果`.ssh` 已经存在了,可以忽略这一项
|
||||
|
||||
为啥用配置ssh公钥呢,同学们记不记得我急使用github上传上传代码的时候也要把自己的公钥配置上github上
|
||||
|
||||
这也是方面每次操作git仓库的时候不用再去输入密码
|
||||
|
||||
```
|
||||
cd ~/
|
||||
mkdir .ssh
|
||||
```
|
||||
|
||||
进入.ssh 文件下,创建一个 `authorized_keys` 文件,这个文件就是后面就是要放我们客户端的公钥
|
||||
|
||||
```
|
||||
cd ~/.ssh
|
||||
touch authorized_keys
|
||||
```
|
||||
|
||||
别忘了`authorized_keys`给设置权限,很多同学发现自己不能免密登陆,都是因为忘记了给`authorized_keys` 设置权限
|
||||
|
||||
```
|
||||
chmod 700 /home/git/.ssh
|
||||
chmod 600 /home/git/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
接下来我们要把客户端的公钥放在git服务器上,我们在回到客户端,创建一个公钥
|
||||
|
||||
在我们自己的电脑上,有公钥和私钥 两个文件分别是:`id_rsa` 和 `id_rsa.pub`
|
||||
|
||||
如果是`windows`系统公钥私钥的目录在`C:\Users\用户名\.ssh` 下
|
||||
|
||||
如果是mac 或者 linux, 公钥和私钥的目录这里 `cd ~/.ssh/`, 如果发现自己的电脑上没有公钥私钥,那就自己创建一个
|
||||
|
||||
创建密钥的命令
|
||||
|
||||
```
|
||||
ssh-keygen -t rsa
|
||||
```
|
||||
|
||||
创建密钥的过程中,一路点击回车就可以了。不需要填任何东西
|
||||
|
||||
把公钥拷贝到git服务器上,将我们刚刚生成的`id_rsa.pub`,拷贝到git服务器的`/home/git/.ssh/`目录
|
||||
|
||||
在git服务器上,将公钥添加到`authorized_keys` 文件中
|
||||
|
||||
```
|
||||
cd /home/git/.ssh/
|
||||
cat id_rsa.pub >> authorized_keys
|
||||
```
|
||||
|
||||
如何看我们配置的密钥是否成功呢, 在客户点直接登录git服务器,看看是否是免密登陆
|
||||
```
|
||||
ssh git@git服务器ip
|
||||
```
|
||||
|
||||
例如:
|
||||
|
||||
```
|
||||
ssh git@127.0.0.1
|
||||
```
|
||||
|
||||
如果可以免密登录,那就说明服务器端密钥配置成功了
|
||||
|
||||
## 服务器端部署Git 仓库
|
||||
|
||||
我们在登陆到git 服务器端,切换为成 git账户
|
||||
|
||||
如果是root账户切换成git账户
|
||||
```
|
||||
su - git
|
||||
```
|
||||
|
||||
如果是其他账户切换为git账户
|
||||
```
|
||||
sudo su - git
|
||||
```
|
||||
|
||||
进入git目录下
|
||||
```
|
||||
cd ~/git
|
||||
```
|
||||
|
||||
创建我们的第一个Git私服的仓库,我们叫它为world仓库
|
||||
|
||||
那么首先创建一个文件夹名为: world.git ,然后进入这个目录
|
||||
|
||||
有同学问,为什么文件夹名字后面要放`.git`, 其实不这样命名也是可以的
|
||||
|
||||
但是细心的同学可能注意到,我们平时在github上 `git clone` 其他人的仓库的时候,仓库名字后面,都是加上`.git`的
|
||||
|
||||
例如下面这个例子,其实就是github对仓库名称的一个命名规则,所以我们也遵守github的命名规则。
|
||||
|
||||
```
|
||||
git clone https://github.com/youngyangyang04/NoSQLAttack.git
|
||||
```
|
||||
|
||||
所以我们的操作是
|
||||
```
|
||||
[git@localhost git]# mkdir world.git
|
||||
[git@localhost git]# cd world.git
|
||||
```
|
||||
|
||||
初始化我们的`world`仓库
|
||||
|
||||
```
|
||||
git init --bare
|
||||
|
||||
```
|
||||
|
||||
**如果我们想创建多个仓库,就在这里创建多个文件夹并初始化就可以了,和world仓库的操作过程是一样一样的**
|
||||
|
||||
现在我们服务端的git仓库就部署完了,接下来就看看客户端,如何使用这个仓库呢
|
||||
|
||||
## 客户端连接远程仓库
|
||||
|
||||
我们在自己的电脑上创建一个文件夹 也叫做`world`吧
|
||||
|
||||
其实这里命名是随意的,但是我们为了和git服务端的仓库名称保持同步。 这样更直观我们操作的是哪一个仓库。
|
||||
|
||||
```
|
||||
mkdir world
|
||||
cd world
|
||||
```
|
||||
|
||||
进入world文件,并初始化操作
|
||||
|
||||
```
|
||||
cd world
|
||||
git init
|
||||
```
|
||||
|
||||
在world目录上创建一个测试文件,并且将其添加到git版本管理中
|
||||
|
||||
```
|
||||
touch test
|
||||
git add test
|
||||
git commit -m "add test file"
|
||||
```
|
||||
|
||||
将次仓库和远端仓库同步
|
||||
|
||||
```
|
||||
git remote add origin git@git服务器端的ip:world.git
|
||||
git push -u origin master
|
||||
```
|
||||
|
||||
此时这个test测试文件就已经提交到我们的git远端私服上了
|
||||
|
||||
## Git私服安全问题
|
||||
|
||||
这里有两点安全问题
|
||||
|
||||
### linux git的密码不要泄露出去
|
||||
|
||||
否则,别人可以通过 ssh git@git服务器IP 来登陆到你的git私服服务器上
|
||||
|
||||
当然了,这里同学们如果买的是云厂商的云服务器的话
|
||||
|
||||
如果有人恶意想通过 尝试不同密码链接的方式来链接你的服务器,重试三次以上
|
||||
|
||||
这个客户端的IP就会被封掉,同时邮件通知我们可以IP来自哪里
|
||||
|
||||
所以大可放心 密码只要我们不泄露出去,基本上不会有人同时不断尝试密码的方式来登上我们的git私服服务器
|
||||
|
||||
### 私钥文件`id_rsa` 不要给别人
|
||||
|
||||
如果有人得到了这个私钥,就可以免密码登陆我们的git私服上了,我相信大家也不至于把自己的私钥主动给别人吧
|
||||
|
||||
## 总结
|
||||
|
||||
这里就是整个git私服搭建的全过程,安全问题我也给大家列举了出来,接下来好好享受自己的Git私服吧
|
||||
|
||||
**enjoy!**
|
||||
|
129
problems/qita/server.md
Normal file
129
problems/qita/server.md
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
# 一台服务器有什么用!
|
||||
|
||||
* [阿里云活动期间服务器购买](https://www.aliyun.com/minisite/goods?taskCode=shareNew2205&recordId=3641992&userCode=roof0wob)
|
||||
* [腾讯云活动期间服务器购买](https://curl.qcloud.com/EiaMXllu)
|
||||
|
||||
但在组织这场活动的时候,了解到大家都有一个共同的问题: **这个服务器究竟有啥用??**
|
||||
|
||||
这真是一个好问题,而且我一句两句还说不清楚,所以就专门发文来讲一讲。
|
||||
|
||||
同时我还录制的一期视频,哈哈我的视频号,大家可以关注一波。
|
||||
|
||||
|
||||
一说到服务器,可能很多人都说搞分布式,做计算,搞爬虫,做程序后台服务,多人合作等等。
|
||||
|
||||
其实这些普通人都用不上,我来说一说大家能用上的吧。
|
||||
|
||||
## 搭建git私服
|
||||
|
||||
大家平时工作的时候一定有一个自己的工作文件夹,学生的话就是自己的课件,考试,准备面试的资料等等。
|
||||
|
||||
已经工作的录友,会有一个文件夹放着自己重要的文档,Markdown,图片,简历等等。
|
||||
|
||||
这么重要的文件夹,而且我们每天都要更新,也担心哪天电脑丢了,或者坏了,突然这些都不见了。
|
||||
|
||||
所以我们想备份嘛。
|
||||
|
||||
还有就是我们经常个人电脑和工作电脑要同步一些私人资料,而不是用微信传来传去。
|
||||
|
||||
这些都是git私服的使用场景,而且很好用。
|
||||
|
||||
大家也知道 github,gitee也可以搞私人仓库 用来备份,同步文件,但自己的文档可能放着很多重要的信息,包括自己的各种密码,密钥之类的,放到上面未必安全。你就不怕哪些重大bug把你的信息都泄漏了么[机智]
|
||||
|
||||
更关键的是,github 和 gitee都限速的。毕竟人家的功能定位并不是网盘。
|
||||
|
||||
项目里有大文件(几百M以上),例如pdf,ppt等等 其上传和下载速度会让你窒息。
|
||||
|
||||
**后面我会发文专门来讲一讲,如何大家git私服!**
|
||||
|
||||
## 搞一个文件存储
|
||||
|
||||
这个可以用来生成文件的下载链接,也可以把本地文件传到服务器上。
|
||||
|
||||
相当于自己做一个对象存储,其实云厂商也有对象存储的产品。
|
||||
|
||||
不过我们自己也可以做一个,不够很多很同学应该都不知道对象存储怎么用吧,其实我们用服务器可以自己做一个类似的公司。
|
||||
|
||||
我现在就用自己用go写的一个工具,部署在服务器上。 用来和服务器传文件,或者生成一些文件的临时下载链接。
|
||||
|
||||
这些都是直接命令行操作的,
|
||||
|
||||
操作方式这样,我把命令包 包装成一个shell命令,想传那个文件,直接 uploadtomyserver,然后就返回可以下载的链接,这个文件也同时传到了我的服务器上。
|
||||
|
||||

|
||||
|
||||
我也把我的项目代码放在了github上:
|
||||
|
||||
https://github.com/youngyangyang04/fileHttpServer
|
||||
|
||||
感兴趣的录友可以去学习一波,顺便给个star 哈哈
|
||||
|
||||
|
||||
## 网站
|
||||
|
||||
做网站,例如 大家知道用html 写几行代码,就可以生成一个网页,但怎么给别人展示呢?
|
||||
|
||||
大家如果用自己的电脑做服务器,只能同一个路由器下的设备可以访问你的网站,可能这个设备出了这个屋子 都访问不了你的网站了。
|
||||
|
||||
因为你的IP不是公网IP。
|
||||
|
||||
如果有了一台云服务器,都是配公网IP,你的网站就可以让任何人访问了。
|
||||
|
||||
或者说 你提供的一个服务就可以让任何人使用。
|
||||
|
||||
例如第二个例子中,我们可以自己开发一个文件存储,这个服务,我只把把命令行给其他人,其他人都可以使用我的服务来生成链接,当然他们的文件也都传到了我的服务器上。
|
||||
|
||||
再说一个使用场景。
|
||||
|
||||
我之前在组织免费里服务器的活动的时候,阿里云给我一个excel,让面就是从我这里买服务器录友的名单,我直接把这个名单甩到群里,让大家自己检查,出现在名单里就可以找我返现,这样做是不是也可以。
|
||||
|
||||
这么做有几个很大的问题:
|
||||
* 大家都要去下载excel,做对比,会有人改excel的内容然后就说是从你这里买的,我不可能挨个去比较excel有没有改动
|
||||
* excel有其他人的个人信息,这是不能暴漏的。
|
||||
* 如果每个人自己用excel查询,私信我返现,一个将近两千人找我返现,我微信根本处理不过来,这就变成体力活了。
|
||||
|
||||
那应该怎么做呢,
|
||||
|
||||
我就简单写一个查询的页面,后端逻辑就是读一个execel表格,大家在查询页面输入自己的阿里云ID,如果在excel里,页面就会返回返现群的二维码,大家就可以自主扫码加群了。
|
||||
|
||||
这样,我最后就直接在返现群里 发等额红包就好了,是不是极大降低人力成本了
|
||||
|
||||
当然我是把 17个返现群的二维码都生成好了,按照一定的规则,展现给查询通过的录友。
|
||||
|
||||
就是这样一个非常普通的查询页面。
|
||||
|
||||

|
||||
|
||||
查询通过之后,就会展现返现群二维码。
|
||||
|
||||

|
||||
|
||||
但要部署在服务器上,因为没有公网IP,别人用不了你的服务。
|
||||
|
||||
|
||||
## 学习linux
|
||||
|
||||
学习linux其实在自己的电脑上搞一台虚拟机,或者安装双系统也可以学习,不过这很考验你的电脑性能如何了。
|
||||
|
||||
如果你有一个服务器,那就是独立的一台电脑,你怎么霍霍就怎么霍霍,而且一年都不用关机的,可以一直跑你的任务,和你本地电脑也完全隔离。
|
||||
|
||||
更方便的是,你目前系统假如是centos,想做一个实验需要在unbantu上,如果是云服务器,更换系统就是在 后台点一下,一键重装,云厂商基本都是支持所有系统一件安装的。
|
||||
|
||||
我们平时自己玩linux经常是配各种环境,然后这个linux就被自己玩坏了(一般都是毫无节制使用root权限导致的),总之就是环境配不起来了,基本就要重装了。
|
||||
|
||||
那云服务器重装系统可太方便了。
|
||||
|
||||
还有就是加入你好不容易配好的环境,如果以后把这个环境玩坏了,你先回退这之前配好的环境而不是重装系统在重新配一遍吧。
|
||||
|
||||
那么可以用云服务器的镜像保存功能,就是你配好环境的那一刻就可以打一个镜像包,以后如果环境坏了,直接回退到上次镜像包的状态,这是不是就很香了。
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
其实云服务器还有很多其他用处,不过我就说一说大家普遍能用的上的。
|
||||
|
||||
|
||||
* [阿里云活动期间服务器购买](https://www.aliyun.com/minisite/goods?taskCode=shareNew2205&recordId=3641992&userCode=roof0wob)
|
||||
* [腾讯云活动期间服务器购买](https://curl.qcloud.com/EiaMXllu)
|
||||
|
@ -258,6 +258,13 @@ class TreeNode<T> {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
class TreeNode(_value: Int = 0, _left: TreeNode = null, _right: TreeNode = null) {
|
||||
var value: Int = _value
|
||||
var left: TreeNode = _left
|
||||
var right: TreeNode = _right
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -591,6 +591,80 @@ function postorderTraversal(root: TreeNode | null): number[] {
|
||||
return res;
|
||||
};
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
// 前序遍历
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def preorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
if (root != null) stack.push(root)
|
||||
while (!stack.isEmpty) {
|
||||
var curNode = stack.top
|
||||
if (curNode != null) {
|
||||
stack.pop()
|
||||
if (curNode.right != null) stack.push(curNode.right)
|
||||
if (curNode.left != null) stack.push(curNode.left)
|
||||
stack.push(curNode)
|
||||
stack.push(null)
|
||||
} else {
|
||||
stack.pop()
|
||||
res.append(stack.pop().value)
|
||||
}
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
|
||||
// 中序遍历
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def inorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
if (root != null) stack.push(root)
|
||||
while (!stack.isEmpty) {
|
||||
var curNode = stack.top
|
||||
if (curNode != null) {
|
||||
stack.pop()
|
||||
if (curNode.right != null) stack.push(curNode.right)
|
||||
stack.push(curNode)
|
||||
stack.push(null)
|
||||
if (curNode.left != null) stack.push(curNode.left)
|
||||
} else {
|
||||
// 等于空的时候好办,弹出这个元素
|
||||
stack.pop()
|
||||
res.append(stack.pop().value)
|
||||
}
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
|
||||
// 后序遍历
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def postorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
if (root != null) stack.push(root)
|
||||
while (!stack.isEmpty) {
|
||||
var curNode = stack.top
|
||||
if (curNode != null) {
|
||||
stack.pop()
|
||||
stack.push(curNode)
|
||||
stack.push(null)
|
||||
if (curNode.right != null) stack.push(curNode.right)
|
||||
if (curNode.left != null) stack.push(curNode.left)
|
||||
} else {
|
||||
stack.pop()
|
||||
res.append(stack.pop().value)
|
||||
}
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -568,6 +568,71 @@ func inorderTraversal(_ root: TreeNode?) -> [Int] {
|
||||
return result
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
// 前序遍历(迭代法)
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def preorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
if (root == null) return res.toList
|
||||
// 声明一个栈,泛型为TreeNode
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
stack.push(root) // 先把根节点压入栈
|
||||
while (!stack.isEmpty) {
|
||||
var curNode = stack.pop()
|
||||
res.append(curNode.value) // 先把这个值压入栈
|
||||
// 如果当前节点的左右节点不为空,则入栈,先放右节点,再放左节点
|
||||
if (curNode.right != null) stack.push(curNode.right)
|
||||
if (curNode.left != null) stack.push(curNode.left)
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
|
||||
// 中序遍历(迭代法)
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def inorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ArrayBuffer[Int]()
|
||||
if (root == null) return res.toList
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
var curNode = root
|
||||
// 将左节点都入栈,当遍历到最左(到空)的时候,再弹出栈顶元素,加入res
|
||||
// 再把栈顶元素的右节点加进来,继续下一轮遍历
|
||||
while (curNode != null || !stack.isEmpty) {
|
||||
if (curNode != null) {
|
||||
stack.push(curNode)
|
||||
curNode = curNode.left
|
||||
} else {
|
||||
curNode = stack.pop()
|
||||
res.append(curNode.value)
|
||||
curNode = curNode.right
|
||||
}
|
||||
}
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
|
||||
// 后序遍历(迭代法)
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def postorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = mutable.ListBuffer[Int]()
|
||||
if (root == null) return res.toList
|
||||
val stack = mutable.Stack[TreeNode]()
|
||||
stack.push(root)
|
||||
while (!stack.isEmpty) {
|
||||
val curNode = stack.pop()
|
||||
res.append(curNode.value)
|
||||
// 这次左节点先入栈,右节点再入栈
|
||||
if(curNode.left != null) stack.push(curNode.left)
|
||||
if(curNode.right != null) stack.push(curNode.right)
|
||||
}
|
||||
// 最后需要翻转List
|
||||
res.reverse.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -470,5 +470,56 @@ func postorder(_ root: TreeNode?, res: inout [Int]) {
|
||||
res.append(root!.val)
|
||||
}
|
||||
```
|
||||
Scala: 前序遍历:(144.二叉树的前序遍历)
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def preorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = ListBuffer[Int]()
|
||||
def traversal(curNode: TreeNode): Unit = {
|
||||
if(curNode == null) return
|
||||
res.append(curNode.value)
|
||||
traversal(curNode.left)
|
||||
traversal(curNode.right)
|
||||
}
|
||||
traversal(root)
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
中序遍历:(94. 二叉树的中序遍历)
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def inorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = ListBuffer[Int]()
|
||||
def traversal(curNode: TreeNode): Unit = {
|
||||
if(curNode == null) return
|
||||
traversal(curNode.left)
|
||||
res.append(curNode.value)
|
||||
traversal(curNode.right)
|
||||
}
|
||||
traversal(root)
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
后序遍历:(145. 二叉树的后序遍历)
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def postorderTraversal(root: TreeNode): List[Int] = {
|
||||
val res = ListBuffer[Int]()
|
||||
def traversal(curNode: TreeNode): Unit = {
|
||||
if (curNode == null) return
|
||||
traversal(curNode.left)
|
||||
traversal(curNode.right)
|
||||
res.append(curNode.value)
|
||||
}
|
||||
traversal(root)
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -467,6 +467,42 @@ object Solution {
|
||||
def replaceSpace(s: String): String = {
|
||||
s.map(c => if(c == ' ') "%20" else c).mkString
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
PHP:
|
||||
```php
|
||||
function replaceSpace($s){
|
||||
$sLen = strlen($s);
|
||||
$moreLen = $this->spaceLen($s) * 2;
|
||||
|
||||
$head = $sLen - 1;
|
||||
$tail = $sLen + $moreLen - 1;
|
||||
|
||||
$s = $s . str_repeat(' ', $moreLen);
|
||||
while ($head != $tail) {
|
||||
if ($s[$head] == ' ') {
|
||||
$s[$tail--] = '0';
|
||||
$s[$tail--] = '2';
|
||||
$s[$tail] = '%';
|
||||
} else {
|
||||
$s[$tail] = $s[$head];
|
||||
}
|
||||
$head--;
|
||||
$tail--;
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
// 统计空格个数
|
||||
function spaceLen($s){
|
||||
$count = 0;
|
||||
for ($i = 0; $i < strlen($s); $i++) {
|
||||
if ($s[$i] == ' ') {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -365,6 +365,84 @@ class Solution:
|
||||
return res
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
**90.子集II**
|
||||
|
||||
```javascript
|
||||
function subsetsWithDup(nums) {
|
||||
nums.sort((a, b) => a - b);
|
||||
const resArr = [];
|
||||
backTraking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTraking(nums, startIndex, route) {
|
||||
resArr.push([...route]);
|
||||
const helperSet = new Set();
|
||||
for (let i = startIndex, length = nums.length; i < length; i++) {
|
||||
if (helperSet.has(nums[i])) continue;
|
||||
helperSet.add(nums[i]);
|
||||
route.push(nums[i]);
|
||||
backTraking(nums, i + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**40. 组合总和 II**
|
||||
|
||||
```javascript
|
||||
function combinationSum2(candidates, target) {
|
||||
candidates.sort((a, b) => a - b);
|
||||
const resArr = [];
|
||||
backTracking(candidates, target, 0, 0, []);
|
||||
return resArr;
|
||||
function backTracking( candidates, target, curSum, startIndex, route ) {
|
||||
if (curSum > target) return;
|
||||
if (curSum === target) {
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
const helperSet = new Set();
|
||||
for (let i = startIndex, length = candidates.length; i < length; i++) {
|
||||
let tempVal = candidates[i];
|
||||
if (helperSet.has(tempVal)) continue;
|
||||
helperSet.add(tempVal);
|
||||
route.push(tempVal);
|
||||
backTracking(candidates, target, curSum + tempVal, i + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**47. 全排列 II**
|
||||
|
||||
```javaescript
|
||||
function permuteUnique(nums) {
|
||||
const resArr = [];
|
||||
const usedArr = [];
|
||||
backTracking(nums, []);
|
||||
return resArr;
|
||||
function backTracking(nums, route) {
|
||||
if (nums.length === route.length) {
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
const usedSet = new Set();
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
if (usedArr[i] === true || usedSet.has(nums[i])) continue;
|
||||
usedSet.add(nums[i]);
|
||||
route.push(nums[i]);
|
||||
usedArr[i] = true;
|
||||
backTracking(nums, route);
|
||||
usedArr[i] = false;
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
**90.子集II**
|
||||
@ -376,7 +454,7 @@ function subsetsWithDup(nums: number[]): number[][] {
|
||||
backTraking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTraking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
const helperSet: Set<number> = new Set();
|
||||
for (let i = startIndex, length = nums.length; i < length; i++) {
|
||||
if (helperSet.has(nums[i])) continue;
|
||||
@ -403,7 +481,7 @@ function combinationSum2(candidates: number[], target: number): number[][] {
|
||||
) {
|
||||
if (curSum > target) return;
|
||||
if (curSum === target) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
const helperSet: Set<number> = new Set();
|
||||
@ -430,7 +508,7 @@ function permuteUnique(nums: number[]): number[][] {
|
||||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (nums.length === route.length) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
const usedSet: Set<number> = new Set();
|
||||
|
Reference in New Issue
Block a user