diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md
index 4b50d666..f7142bad 100644
--- a/problems/0027.移除元素.md
+++ b/problems/0027.移除元素.md
@@ -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
/**
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
@@ -328,6 +338,20 @@ int removeElement(int* nums, int numsSize, int val){
return slow;
}
```
+
+
+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 {
@@ -343,5 +367,6 @@ object Solution {
}
}
```
+
-----------------------
diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md
index 6c04e7de..5cf44ded 100644
--- a/problems/0035.搜索插入位置.md
+++ b/problems/0035.搜索插入位置.md
@@ -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;
}
};
diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md
index 1743243d..3aa17699 100644
--- a/problems/0102.二叉树的层序遍历.md
+++ b/problems/0102.二叉树的层序遍历.md
@@ -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
+ }
+}
+```
# 总结
diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md
index d98ff8a9..b7598365 100644
--- a/problems/0110.平衡二叉树.md
+++ b/problems/0110.平衡二叉树.md
@@ -208,7 +208,7 @@ int getHeight(TreeNode* node) {
```CPP
class Solution {
public:
- // 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
+ // 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md
index a3ebe24d..af5b8043 100644
--- a/problems/0226.翻转二叉树.md
+++ b/problems/0226.翻转二叉树.md
@@ -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)
diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md
index cfa8ae12..ffa66c02 100644
--- a/problems/0300.最长上升子序列.md
+++ b/problems/0300.最长上升子序列.md
@@ -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;
+};
+```
+
diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md
index 4f571d09..b1ed80c3 100644
--- a/problems/0674.最长连续递增序列.md
+++ b/problems/0674.最长连续递增序列.md
@@ -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;
+};
+```
+
diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md
index 1e474f9a..50f01226 100644
--- a/problems/0704.二分查找.md
+++ b/problems/0704.二分查找.md
@@ -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 {
}
```
+
-----------------------
diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md
index 87b1492a..0b7b5199 100644
--- a/problems/0718.最长重复子数组.md
+++ b/problems/0718.最长重复子数组.md
@@ -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;
+};
+```
+
+
+
-----------------------
diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md
index 0e79a3d6..20bdf7b0 100644
--- a/problems/0977.有序数组的平方.md
+++ b/problems/0977.有序数组的平方.md
@@ -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:
双指针:
diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md
index ecedf89b..d58330ec 100644
--- a/problems/1143.最长公共子序列.md
+++ b/problems/1143.最长公共子序列.md
@@ -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];
+};
+```
+
+
+
-----------------------
diff --git a/problems/qita/gitserver.md b/problems/qita/gitserver.md
new file mode 100644
index 00000000..9ee06ae4
--- /dev/null
+++ b/problems/qita/gitserver.md
@@ -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[=]] [--html-path] [--man-path] [--info-path]
+ [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
+ [--git-dir=] [--work-tree=] [--namespace=]
+ []
+
+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 ' or 'git help '
+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!**
+
diff --git a/problems/qita/server.md b/problems/qita/server.md
new file mode 100644
index 00000000..16995d70
--- /dev/null
+++ b/problems/qita/server.md
@@ -0,0 +1,126 @@
+
+# 一台服务器有什么用!
+
+但在组织这场活动的时候,了解到大家都有一个共同的问题: **这个服务器究竟有啥用??**
+
+这真是一个好问题,而且我一句两句还说不清楚,所以就专门发文来讲一讲。
+
+同时我还录制的一期视频,哈哈我的视频号,大家可以关注一波。
+
+
+一说到服务器,可能很多人都说搞分布式,做计算,搞爬虫,做程序后台服务,多人合作等等。
+
+其实这些普通人都用不上,我来说一说大家能用上的吧。
+
+## 搭建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)
+
diff --git a/problems/其他/参与本项目.md b/problems/qita/参与本项目.md
similarity index 100%
rename from problems/其他/参与本项目.md
rename to problems/qita/参与本项目.md
diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md
index 9c151e32..9e10ac20 100644
--- a/problems/二叉树理论基础.md
+++ b/problems/二叉树理论基础.md
@@ -258,6 +258,13 @@ class TreeNode {
}
}
```
-
+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
+}
+```
-----------------------
diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md
index f6edf586..9ca6ac39 100644
--- a/problems/二叉树的统一迭代法.md
+++ b/problems/二叉树的统一迭代法.md
@@ -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
+ }
+}
+```
-----------------------
diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md
index 13ba5f1e..fac30f99 100644
--- a/problems/二叉树的迭代遍历.md
+++ b/problems/二叉树的迭代遍历.md
@@ -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
+ }
+}
+```
-----------------------
diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md
index 186c39d3..29c0cfda 100644
--- a/problems/二叉树的递归遍历.md
+++ b/problems/二叉树的递归遍历.md
@@ -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
+ }
+}
+```
-----------------------