mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-07 15:45:40 +08:00
Merge branch 'master' into master
This commit is contained in:
@ -275,5 +275,48 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
// 导入包
|
||||
import scala.collection.mutable
|
||||
def twoSum(nums: Array[Int], target: Int): Array[Int] = {
|
||||
// key存储值,value存储下标
|
||||
val map = new mutable.HashMap[Int, Int]()
|
||||
for (i <- nums.indices) {
|
||||
val tmp = target - nums(i) // 计算差值
|
||||
// 如果这个差值存在于map,则说明找到了结果
|
||||
if (map.contains(tmp)) {
|
||||
return Array(map.get(tmp).get, i)
|
||||
}
|
||||
// 如果不包含把当前值与其下标放到map
|
||||
map.put(nums(i), i)
|
||||
}
|
||||
// 如果没有找到直接返回一个空的数组,return关键字可以省略
|
||||
new Array[Int](2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public class Solution {
|
||||
public int[] TwoSum(int[] nums, int target) {
|
||||
Dictionary<int ,int> dic= new Dictionary<int,int>();
|
||||
for(int i=0;i<nums.Length;i++){
|
||||
int imp= target-nums[i];
|
||||
if(dic.ContainsKey(imp)&&dic[imp]!=i){
|
||||
return new int[]{i, dic[imp]};
|
||||
}
|
||||
if(!dic.ContainsKey(nums[i])){
|
||||
dic.Add(nums[i],i);
|
||||
}
|
||||
}
|
||||
return new int[]{0, 0};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -616,6 +616,49 @@ public class Solution
|
||||
}
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
// 导包
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.control.Breaks.{break, breakable}
|
||||
|
||||
def threeSum(nums: Array[Int]): List[List[Int]] = {
|
||||
// 定义结果集,最后需要转换为List
|
||||
val res = ListBuffer[List[Int]]()
|
||||
val nums_tmp = nums.sorted // 对nums进行排序
|
||||
for (i <- nums_tmp.indices) {
|
||||
// 如果要排的第一个数字大于0,直接返回结果
|
||||
if (nums_tmp(i) > 0) {
|
||||
return res.toList
|
||||
}
|
||||
// 如果i大于0并且和前一个数字重复,则跳过本次循环,相当于continue
|
||||
breakable {
|
||||
if (i > 0 && nums_tmp(i) == nums_tmp(i - 1)) {
|
||||
break
|
||||
} else {
|
||||
var left = i + 1
|
||||
var right = nums_tmp.length - 1
|
||||
while (left < right) {
|
||||
var sum = nums_tmp(i) + nums_tmp(left) + nums_tmp(right) // 求三数之和
|
||||
if (sum < 0) left += 1
|
||||
else if (sum > 0) right -= 1
|
||||
else {
|
||||
res += List(nums_tmp(i), nums_tmp(left), nums_tmp(right)) // 如果等于0 添加进结果集
|
||||
// 为了避免重复,对left和right进行移动
|
||||
while (left < right && nums_tmp(left) == nums_tmp(left + 1)) left += 1
|
||||
while (left < right && nums_tmp(right) == nums_tmp(right - 1)) right -= 1
|
||||
left += 1
|
||||
right -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 最终返回需要转换为List,return关键字可以省略
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -522,6 +522,49 @@ public class Solution
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
// 导包
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.control.Breaks.{break, breakable}
|
||||
def fourSum(nums: Array[Int], target: Int): List[List[Int]] = {
|
||||
val res = ListBuffer[List[Int]]()
|
||||
val nums_tmp = nums.sorted // 先排序
|
||||
for (i <- nums_tmp.indices) {
|
||||
breakable {
|
||||
if (i > 0 && nums_tmp(i) == nums_tmp(i - 1)) {
|
||||
break // 如果该值和上次的值相同,跳过本次循环,相当于continue
|
||||
} else {
|
||||
for (j <- i + 1 until nums_tmp.length) {
|
||||
breakable {
|
||||
if (j > i + 1 && nums_tmp(j) == nums_tmp(j - 1)) {
|
||||
break // 同上
|
||||
} else {
|
||||
// 双指针
|
||||
var (left, right) = (j + 1, nums_tmp.length - 1)
|
||||
while (left < right) {
|
||||
var sum = nums_tmp(i) + nums_tmp(j) + nums_tmp(left) + nums_tmp(right)
|
||||
if (sum == target) {
|
||||
// 满足要求,直接加入到集合里面去
|
||||
res += List(nums_tmp(i), nums_tmp(j), nums_tmp(left), nums_tmp(right))
|
||||
while (left < right && nums_tmp(left) == nums_tmp(left + 1)) left += 1
|
||||
while (left < right && nums_tmp(right) == nums_tmp(right - 1)) right -= 1
|
||||
left += 1
|
||||
right -= 1
|
||||
} else if (sum < target) left += 1
|
||||
else right -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 最终返回的res要转换为List,return关键字可以省略
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -289,6 +289,28 @@ func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
|
||||
return dummyHead.next
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def removeNthFromEnd(head: ListNode, n: Int): ListNode = {
|
||||
val dummy = new ListNode(-1, head) // 定义虚拟头节点
|
||||
var fast = head // 快指针从头开始走
|
||||
var slow = dummy // 慢指针从虚拟头开始头
|
||||
// 因为参数 n 是不可变量,所以不能使用 while(n>0){n-=1}的方式
|
||||
for (i <- 0 until n) {
|
||||
fast = fast.next
|
||||
}
|
||||
// 快指针和满指针一起走,直到fast走到null
|
||||
while (fast != null) {
|
||||
slow = slow.next
|
||||
fast = fast.next
|
||||
}
|
||||
// 删除slow的下一个节点
|
||||
slow.next = slow.next.next
|
||||
// 返回虚拟头节点的下一个
|
||||
dummy.next
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -400,6 +400,27 @@ bool isValid(char * s){
|
||||
return !stackTop;
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def isValid(s: String): Boolean = {
|
||||
if(s.length % 2 != 0) return false // 如果字符串长度是奇数直接返回false
|
||||
val stack = mutable.Stack[Char]()
|
||||
// 循环遍历字符串
|
||||
for (i <- s.indices) {
|
||||
val c = s(i)
|
||||
if (c == '(' || c == '[' || c == '{') stack.push(c)
|
||||
else if(stack.isEmpty) return false // 如果没有(、[、{则直接返回false
|
||||
// 以下三种情况,不满足则直接返回false
|
||||
else if(c==')' && stack.pop() != '(') return false
|
||||
else if(c==']' && stack.pop() != '[') return false
|
||||
else if(c=='}' && stack.pop() != '{') return false
|
||||
}
|
||||
// 如果为空则正确匹配,否则还有余孽就不匹配
|
||||
stack.isEmpty
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -311,7 +311,29 @@ func swapPairs(_ head: ListNode?) -> ListNode? {
|
||||
return dummyHead.next
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
// 虚拟头节点
|
||||
object Solution {
|
||||
def swapPairs(head: ListNode): ListNode = {
|
||||
var dummy = new ListNode(0, head) // 虚拟头节点
|
||||
var pre = dummy
|
||||
var cur = head
|
||||
// 当pre的下一个和下下个都不为空,才进行两两转换
|
||||
while (pre.next != null && pre.next.next != null) {
|
||||
var tmp: ListNode = cur.next.next // 缓存下一次要进行转换的第一个节点
|
||||
pre.next = cur.next // 步骤一
|
||||
cur.next.next = cur // 步骤二
|
||||
cur.next = tmp // 步骤三
|
||||
// 下面是准备下一轮的交换
|
||||
pre = cur
|
||||
cur = tmp
|
||||
}
|
||||
// 最终返回dummy虚拟头节点的下一个,return可以省略
|
||||
dummy.next
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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,7 @@ int removeElement(int* nums, int numsSize, int val){
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
fun removeElement(nums: IntArray, `val`: Int): Int {
|
||||
@ -340,5 +351,22 @@ fun removeElement(nums: IntArray, `val`: Int): Int {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def removeElement(nums: Array[Int], `val`: Int): Int = {
|
||||
var slow = 0
|
||||
for (fast <- 0 until nums.length) {
|
||||
if (`val` != nums(fast)) {
|
||||
nums(slow) = nums(fast)
|
||||
slow += 1
|
||||
}
|
||||
}
|
||||
slow
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -1166,5 +1166,80 @@ func strStr(_ haystack: String, _ needle: String) -> Int {
|
||||
|
||||
```
|
||||
|
||||
PHP:
|
||||
|
||||
> 前缀表统一减一
|
||||
```php
|
||||
function strStr($haystack, $needle) {
|
||||
if (strlen($needle) == 0) return 0;
|
||||
$next= [];
|
||||
$this->getNext($next,$needle);
|
||||
|
||||
$j = -1;
|
||||
for ($i = 0;$i < strlen($haystack); $i++) { // 注意i就从0开始
|
||||
while($j >= 0 && $haystack[$i] != $needle[$j + 1]) {
|
||||
$j = $next[$j];
|
||||
}
|
||||
if ($haystack[$i] == $needle[$j + 1]) {
|
||||
$j++;
|
||||
}
|
||||
if ($j == (strlen($needle) - 1) ) {
|
||||
return ($i - strlen($needle) + 1);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getNext(&$next, $s){
|
||||
$j = -1;
|
||||
$next[0] = $j;
|
||||
for($i = 1; $i < strlen($s); $i++) { // 注意i从1开始
|
||||
while ($j >= 0 && $s[$i] != $s[$j + 1]) {
|
||||
$j = $next[$j];
|
||||
}
|
||||
if ($s[$i] == $s[$j + 1]) {
|
||||
$j++;
|
||||
}
|
||||
$next[$i] = $j;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 前缀表统一不减一
|
||||
```php
|
||||
function strStr($haystack, $needle) {
|
||||
if (strlen($needle) == 0) return 0;
|
||||
$next= [];
|
||||
$this->getNext($next,$needle);
|
||||
|
||||
$j = 0;
|
||||
for ($i = 0;$i < strlen($haystack); $i++) { // 注意i就从0开始
|
||||
while($j > 0 && $haystack[$i] != $needle[$j]) {
|
||||
$j = $next[$j-1];
|
||||
}
|
||||
if ($haystack[$i] == $needle[$j]) {
|
||||
$j++;
|
||||
}
|
||||
if ($j == strlen($needle)) {
|
||||
return ($i - strlen($needle) + 1);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getNext(&$next, $s){
|
||||
$j = 0;
|
||||
$next[0] = $j;
|
||||
for($i = 1; $i < strlen($s); $i++) { // 注意i从1开始
|
||||
while ($j > 0 && $s[$i] != $s[$j]) {
|
||||
$j = $next[$j-1];
|
||||
}
|
||||
if ($s[$i] == $s[$j]) {
|
||||
$j++;
|
||||
}
|
||||
$next[$i] = $j;
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -480,7 +480,52 @@ var searchRange = function(nums, target) {
|
||||
return [-1, -1];
|
||||
};
|
||||
```
|
||||
### Scala
|
||||
```scala
|
||||
object Solution {
|
||||
def searchRange(nums: Array[Int], target: Int): Array[Int] = {
|
||||
var left = getLeftBorder(nums, target)
|
||||
var right = getRightBorder(nums, target)
|
||||
if (left == -2 || right == -2) return Array(-1, -1)
|
||||
if (right - left > 1) return Array(left + 1, right - 1)
|
||||
Array(-1, -1)
|
||||
}
|
||||
|
||||
// 寻找左边界
|
||||
def getLeftBorder(nums: Array[Int], target: Int): Int = {
|
||||
var leftBorder = -2
|
||||
var left = 0
|
||||
var right = nums.length - 1
|
||||
while (left <= right) {
|
||||
var mid = left + (right - left) / 2
|
||||
if (nums(mid) >= target) {
|
||||
right = mid - 1
|
||||
leftBorder = right
|
||||
} else {
|
||||
left = mid + 1
|
||||
}
|
||||
}
|
||||
leftBorder
|
||||
}
|
||||
|
||||
// 寻找右边界
|
||||
def getRightBorder(nums: Array[Int], target: Int): Int = {
|
||||
var rightBorder = -2
|
||||
var left = 0
|
||||
var right = nums.length - 1
|
||||
while (left <= right) {
|
||||
var mid = left + (right - left) / 2
|
||||
if (nums(mid) <= target) {
|
||||
left = mid + 1
|
||||
rightBorder = left
|
||||
} else {
|
||||
right = mid - 1
|
||||
}
|
||||
}
|
||||
rightBorder
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<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;
|
||||
}
|
||||
};
|
||||
@ -316,7 +316,26 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int {
|
||||
return right + 1
|
||||
}
|
||||
```
|
||||
|
||||
### Scala
|
||||
```scala
|
||||
object Solution {
|
||||
def searchInsert(nums: Array[Int], target: Int): Int = {
|
||||
var left = 0
|
||||
var right = nums.length - 1
|
||||
while (left <= right) {
|
||||
var mid = left + (right - left) / 2
|
||||
if (target == nums(mid)) {
|
||||
return mid
|
||||
} else if (target > nums(mid)) {
|
||||
left = mid + 1
|
||||
} else {
|
||||
right = mid - 1
|
||||
}
|
||||
}
|
||||
right + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PHP
|
||||
|
||||
|
@ -96,7 +96,7 @@ public:
|
||||
vector<vector<int>> merge(vector<vector<int>>& intervals) {
|
||||
vector<vector<int>> result;
|
||||
if (intervals.size() == 0) return result;
|
||||
// 排序的参数使用了lamda表达式
|
||||
// 排序的参数使用了lambda表达式
|
||||
sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});
|
||||
|
||||
result.push_back(intervals[0]);
|
||||
|
@ -130,57 +130,37 @@ Java:
|
||||
```Java
|
||||
class Solution {
|
||||
public int[][] generateMatrix(int n) {
|
||||
int loop = 0; // 控制循环次数
|
||||
int[][] res = new int[n][n];
|
||||
int start = 0; // 每次循环的开始点(start, start)
|
||||
int count = 1; // 定义填充数字
|
||||
int i, j;
|
||||
|
||||
// 循环次数
|
||||
int loop = n / 2;
|
||||
|
||||
// 定义每次循环起始位置
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
|
||||
// 定义偏移量
|
||||
int offset = 1;
|
||||
|
||||
// 定义填充数字
|
||||
int count = 1;
|
||||
|
||||
// 定义中间位置
|
||||
int mid = n / 2;
|
||||
while (loop > 0) {
|
||||
int i = startX;
|
||||
int j = startY;
|
||||
|
||||
while (loop++ < n / 2) { // 判断边界后,loop从1开始
|
||||
// 模拟上侧从左到右
|
||||
for (; j<startY + n -offset; ++j) {
|
||||
res[startX][j] = count++;
|
||||
for (j = start; j < n - loop; j++) {
|
||||
res[start][j] = count++;
|
||||
}
|
||||
|
||||
// 模拟右侧从上到下
|
||||
for (; i<startX + n -offset; ++i) {
|
||||
for (i = start; i < n - loop; i++) {
|
||||
res[i][j] = count++;
|
||||
}
|
||||
|
||||
// 模拟下侧从右到左
|
||||
for (; j > startY; j--) {
|
||||
for (; j >= loop; j--) {
|
||||
res[i][j] = count++;
|
||||
}
|
||||
|
||||
// 模拟左侧从下到上
|
||||
for (; i > startX; i--) {
|
||||
for (; i >= loop; i--) {
|
||||
res[i][j] = count++;
|
||||
}
|
||||
|
||||
loop--;
|
||||
|
||||
startX += 1;
|
||||
startY += 1;
|
||||
|
||||
offset += 2;
|
||||
start++;
|
||||
}
|
||||
|
||||
if (n % 2 == 1) {
|
||||
res[mid][mid] = count;
|
||||
res[start][start] = count;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -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;
|
||||
|
@ -318,6 +318,61 @@ 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:
|
||||
|
||||
```rust
|
||||
pub fn level_order(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<Vec<i32>> {
|
||||
let mut ans = Vec::new();
|
||||
let mut stack = Vec::new();
|
||||
if root.is_none(){
|
||||
return ans;
|
||||
}
|
||||
stack.push(root.unwrap());
|
||||
while stack.is_empty()!= true{
|
||||
let num = stack.len();
|
||||
let mut level = Vec::new();
|
||||
for _i in 0..num{
|
||||
let tmp = stack.remove(0);
|
||||
level.push(tmp.borrow_mut().val);
|
||||
if tmp.borrow_mut().left.is_some(){
|
||||
stack.push(tmp.borrow_mut().left.take().unwrap());
|
||||
}
|
||||
if tmp.borrow_mut().right.is_some(){
|
||||
stack.push(tmp.borrow_mut().right.take().unwrap());
|
||||
}
|
||||
}
|
||||
ans.push(level);
|
||||
}
|
||||
ans
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!**
|
||||
|
||||
@ -548,6 +603,61 @@ 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
|
||||
pub fn level_order(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<Vec<i32>> {
|
||||
let mut ans = Vec::new();
|
||||
let mut stack = Vec::new();
|
||||
if root.is_none(){
|
||||
return ans;
|
||||
}
|
||||
stack.push(root.unwrap());
|
||||
while stack.is_empty()!= true{
|
||||
let num = stack.len();
|
||||
let mut level = Vec::new();
|
||||
for _i in 0..num{
|
||||
let tmp = stack.remove(0);
|
||||
level.push(tmp.borrow_mut().val);
|
||||
if tmp.borrow_mut().left.is_some(){
|
||||
stack.push(tmp.borrow_mut().left.take().unwrap());
|
||||
}
|
||||
if tmp.borrow_mut().right.is_some(){
|
||||
stack.push(tmp.borrow_mut().right.take().unwrap());
|
||||
}
|
||||
}
|
||||
ans.push(level);
|
||||
}
|
||||
ans
|
||||
}
|
||||
```
|
||||
|
||||
# 199.二叉树的右视图
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/binary-tree-right-side-view/)
|
||||
@ -770,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/)
|
||||
@ -1001,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叉树的层序遍历
|
||||
|
||||
@ -1245,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/)
|
||||
@ -1453,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/)
|
||||
@ -1712,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/)
|
||||
@ -1963,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/)
|
||||
@ -2180,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/)
|
||||
@ -2399,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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 总结
|
||||
|
||||
|
@ -192,6 +192,33 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
pub fn max_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
|
||||
if root.is_none(){
|
||||
return 0;
|
||||
}
|
||||
let mut max_depth: i32 = 0;
|
||||
let mut stack = vec![root.unwrap()];
|
||||
while !stack.is_empty() {
|
||||
let num = stack.len();
|
||||
for _i in 0..num{
|
||||
let top = stack.remove(0);
|
||||
if top.borrow_mut().left.is_some(){
|
||||
stack.push(top.borrow_mut().left.take().unwrap());
|
||||
}
|
||||
if top.borrow_mut().right.is_some(){
|
||||
stack.push(top.borrow_mut().right.take().unwrap());
|
||||
}
|
||||
}
|
||||
max_depth+=1;
|
||||
}
|
||||
max_depth
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
那么我们可以顺便解决一下n叉树的最大深度问题
|
||||
|
||||
# 559.n叉树的最大深度
|
||||
|
@ -208,7 +208,7 @@ int getHeight(TreeNode* node) {
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
|
||||
int getHeight(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
|
@ -488,5 +488,69 @@ func minDepth(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
pub fn min_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
|
||||
return Solution::bfs(root)
|
||||
}
|
||||
|
||||
// 递归
|
||||
pub fn dfs(node: Option<Rc<RefCell<TreeNode>>>) -> i32{
|
||||
if node.is_none(){
|
||||
return 0;
|
||||
}
|
||||
let parent = node.unwrap();
|
||||
let left_child = parent.borrow_mut().left.take();
|
||||
let right_child = parent.borrow_mut().right.take();
|
||||
if left_child.is_none() && right_child.is_none(){
|
||||
return 1;
|
||||
}
|
||||
let mut min_depth = i32::MAX;
|
||||
if left_child.is_some(){
|
||||
let left_depth = Solution::dfs(left_child);
|
||||
if left_depth <= min_depth{
|
||||
min_depth = left_depth
|
||||
}
|
||||
}
|
||||
if right_child.is_some(){
|
||||
let right_depth = Solution::dfs(right_child);
|
||||
if right_depth <= min_depth{
|
||||
min_depth = right_depth
|
||||
}
|
||||
}
|
||||
min_depth + 1
|
||||
|
||||
}
|
||||
|
||||
// 迭代
|
||||
pub fn bfs(node: Option<Rc<RefCell<TreeNode>>>) -> i32{
|
||||
let mut min_depth = 0;
|
||||
if node.is_none(){
|
||||
return min_depth
|
||||
}
|
||||
let mut stack = vec![node.unwrap()];
|
||||
while !stack.is_empty(){
|
||||
min_depth += 1;
|
||||
let num = stack.len();
|
||||
for _i in 0..num{
|
||||
let top = stack.remove(0);
|
||||
let left_child = top.borrow_mut().left.take();
|
||||
let right_child = top.borrow_mut().right.take();
|
||||
if left_child.is_none() && right_child.is_none(){
|
||||
return min_depth;
|
||||
}
|
||||
if left_child.is_some(){
|
||||
stack.push(left_child.unwrap());
|
||||
}
|
||||
if right_child.is_some(){
|
||||
stack.push(right_child.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
min_depth
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -426,6 +426,46 @@ var maxProfit = function(prices) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 贪心法
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
if (prices.length === 0) return 0;
|
||||
let buy: number = prices[0];
|
||||
let profitMax: number = 0;
|
||||
for (let i = 1, length = prices.length; i < length; i++) {
|
||||
profitMax = Math.max(profitMax, prices[i] - buy);
|
||||
buy = Math.min(prices[i], buy);
|
||||
}
|
||||
return profitMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
/**
|
||||
dp[i][0]: 第i天持有股票的最大现金
|
||||
dp[i][1]: 第i天不持有股票的最大现金
|
||||
*/
|
||||
const length = prices.length;
|
||||
if (length === 0) return 0;
|
||||
const dp: number[][] = [];
|
||||
dp[0] = [-prices[0], 0];
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i] = [];
|
||||
dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
|
||||
dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
|
||||
}
|
||||
return dp[length - 1][1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -295,6 +295,42 @@ const maxProfit = (prices) => {
|
||||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
/**
|
||||
dp[i][0]: 第i天持有股票
|
||||
dp[i][1]: 第i天不持有股票
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
if (length === 0) return 0;
|
||||
const dp: number[][] = new Array(length).fill(0).map(_ => []);
|
||||
dp[0] = [-prices[0], 0];
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
|
||||
}
|
||||
return dp[length - 1][1];
|
||||
};
|
||||
```
|
||||
|
||||
> 贪心法
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
let resProfit: number = 0;
|
||||
for (let i = 1, length = prices.length; i < length; i++) {
|
||||
if (prices[i] > prices[i - 1]) {
|
||||
resProfit += prices[i] - prices[i - 1];
|
||||
}
|
||||
}
|
||||
return resProfit;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -352,6 +352,36 @@ const maxProfit = prices => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 版本一
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
/**
|
||||
dp[i][0]: 无操作;
|
||||
dp[i][1]: 第一次买入;
|
||||
dp[i][2]: 第一次卖出;
|
||||
dp[i][3]: 第二次买入;
|
||||
dp[i][4]: 第二次卖出;
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
if (length === 0) return 0;
|
||||
const dp: number[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(5).fill(0));
|
||||
dp[0][1] = -prices[0];
|
||||
dp[0][3] = -prices[0];
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i][0] = dp[i - 1][0];
|
||||
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
|
||||
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
|
||||
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
|
||||
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
|
||||
}
|
||||
return Math.max(dp[length - 1][2], dp[length - 1][4]);
|
||||
};
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
> 版本一:
|
||||
|
@ -370,7 +370,31 @@ ListNode *detectCycle(ListNode *head) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def detectCycle(head: ListNode): ListNode = {
|
||||
var fast = head // 快指针
|
||||
var slow = head // 慢指针
|
||||
while (fast != null && fast.next != null) {
|
||||
fast = fast.next.next // 快指针一次走两步
|
||||
slow = slow.next // 慢指针一次走一步
|
||||
// 如果相遇,fast快指针回到头
|
||||
if (fast == slow) {
|
||||
fast = head
|
||||
// 两个指针一步一步的走,第一次相遇的节点必是入环节点
|
||||
while (fast != slow) {
|
||||
fast = fast.next
|
||||
slow = slow.next
|
||||
}
|
||||
return fast
|
||||
}
|
||||
}
|
||||
// 如果fast指向空值,必然无环返回null
|
||||
null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -136,19 +136,19 @@ java:
|
||||
class Solution {
|
||||
public int evalRPN(String[] tokens) {
|
||||
Deque<Integer> stack = new LinkedList();
|
||||
for (int i = 0; i < tokens.length; ++i) {
|
||||
if ("+".equals(tokens[i])) { // leetcode 内置jdk的问题,不能使用==判断字符串是否相等
|
||||
for (String s : tokens) {
|
||||
if ("+".equals(s)) { // leetcode 内置jdk的问题,不能使用==判断字符串是否相等
|
||||
stack.push(stack.pop() + stack.pop()); // 注意 - 和/ 需要特殊处理
|
||||
} else if ("-".equals(tokens[i])) {
|
||||
} else if ("-".equals(s)) {
|
||||
stack.push(-stack.pop() + stack.pop());
|
||||
} else if ("*".equals(tokens[i])) {
|
||||
} else if ("*".equals(s)) {
|
||||
stack.push(stack.pop() * stack.pop());
|
||||
} else if ("/".equals(tokens[i])) {
|
||||
} else if ("/".equals(s)) {
|
||||
int temp1 = stack.pop();
|
||||
int temp2 = stack.pop();
|
||||
stack.push(temp2 / temp1);
|
||||
} else {
|
||||
stack.push(Integer.valueOf(tokens[i]));
|
||||
stack.push(Integer.valueOf(s));
|
||||
}
|
||||
}
|
||||
return stack.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>
|
||||
|
@ -758,7 +758,63 @@ func reverseWord(_ s: inout [Character]) {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseWords(s: String): String = {
|
||||
var sb = removeSpace(s) // 移除多余的空格
|
||||
reverseString(sb, 0, sb.length - 1) // 翻转字符串
|
||||
reverseEachWord(sb)
|
||||
sb.mkString
|
||||
}
|
||||
|
||||
// 移除多余的空格
|
||||
def removeSpace(s: String): Array[Char] = {
|
||||
var start = 0
|
||||
var end = s.length - 1
|
||||
// 移除字符串前面的空格
|
||||
while (start < s.length && s(start) == ' ') start += 1
|
||||
// 移除字符串后面的空格
|
||||
while (end >= 0 && s(end) == ' ') end -= 1
|
||||
var sb = "" // String
|
||||
// 当start小于等于end的时候,执行添加操作
|
||||
while (start <= end) {
|
||||
var c = s(start)
|
||||
// 当前字符不等于空,sb的最后一个字符不等于空的时候添加到sb
|
||||
if (c != ' ' || sb(sb.length - 1) != ' ') {
|
||||
sb ++= c.toString
|
||||
}
|
||||
start += 1 // 指针向右移动
|
||||
}
|
||||
sb.toArray
|
||||
}
|
||||
|
||||
// 翻转字符串
|
||||
def reverseString(s: Array[Char], start: Int, end: Int): Unit = {
|
||||
var (left, right) = (start, end)
|
||||
while (left < right) {
|
||||
var tmp = s(left)
|
||||
s(left) = s(right)
|
||||
s(right) = tmp
|
||||
left += 1
|
||||
right -= 1
|
||||
}
|
||||
}
|
||||
|
||||
// 翻转每个单词
|
||||
def reverseEachWord(s: Array[Char]): Unit = {
|
||||
var i = 0
|
||||
while (i < s.length) {
|
||||
var j = i + 1
|
||||
// 向后迭代寻找每个单词的坐标
|
||||
while (j < s.length && s(j) != ' ') j += 1
|
||||
reverseString(s, i, j - 1) // 翻转每个单词
|
||||
i = j + 1 // i往后更新
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -409,5 +409,27 @@ var maxProfit = function(k, prices) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxProfit(k: number, prices: number[]): number {
|
||||
const length: number = prices.length;
|
||||
if (length === 0) return 0;
|
||||
const dp: number[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(k * 2 + 1).fill(0));
|
||||
for (let i = 1; i <= k; i++) {
|
||||
dp[0][i * 2 - 1] = -prices[0];
|
||||
}
|
||||
for (let i = 1; i < length; i++) {
|
||||
for (let j = 1; j < 2 * k + 1; j++) {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + Math.pow(-1, j) * prices[i]);
|
||||
}
|
||||
}
|
||||
return dp[length - 1][2 * k];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -385,5 +385,61 @@ bool isHappy(int n){
|
||||
return bHappy;
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
// 引入mutable
|
||||
import scala.collection.mutable
|
||||
def isHappy(n: Int): Boolean = {
|
||||
// 存放每次计算后的结果
|
||||
val set: mutable.HashSet[Int] = new mutable.HashSet[Int]()
|
||||
var tmp = n // 因为形参是不可变量,所以需要找到一个临时变量
|
||||
// 开始进入循环
|
||||
while (true) {
|
||||
val sum = getSum(tmp) // 获取这个数每个值的平方和
|
||||
if (sum == 1) return true // 如果最终等于 1,则返回true
|
||||
// 如果set里面已经有这个值了,说明进入无限循环,可以返回false,否则添加这个值到set
|
||||
if (set.contains(sum)) return false
|
||||
else set.add(sum)
|
||||
tmp = sum
|
||||
}
|
||||
// 最终需要返回值,直接返回个false
|
||||
false
|
||||
}
|
||||
|
||||
def getSum(n: Int): Int = {
|
||||
var sum = 0
|
||||
var tmp = n
|
||||
while (tmp != 0) {
|
||||
sum += (tmp % 10) * (tmp % 10)
|
||||
tmp = tmp / 10
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public class Solution {
|
||||
private int getSum(int n) {
|
||||
int sum = 0;
|
||||
//每位数的换算
|
||||
while (n > 0) {
|
||||
sum += (n % 10) * (n % 10);
|
||||
n /= 10;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
public bool IsHappy(int n) {
|
||||
HashSet <int> set = new HashSet<int>();
|
||||
while(n != 1 && !set.Contains(n)) { //判断避免循环
|
||||
set.Add(n);
|
||||
n = getSum(n);
|
||||
}
|
||||
return n == 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -266,6 +266,27 @@ public ListNode removeElements(ListNode head, int val) {
|
||||
}
|
||||
return head;
|
||||
}
|
||||
/**
|
||||
* 不添加虚拟节点and pre Node方式
|
||||
* 时间复杂度 O(n)
|
||||
* 空间复杂度 O(1)
|
||||
* @param head
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public ListNode removeElements(ListNode head, int val) {
|
||||
while(head!=null && head.val==val){
|
||||
head = head.next;
|
||||
}
|
||||
ListNode curr = head;
|
||||
while(curr!=null){
|
||||
while(curr.next!=null && curr.next.val == val){
|
||||
curr.next = curr.next.next;
|
||||
}
|
||||
curr = curr.next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
@ -496,6 +496,40 @@ struct ListNode* reverseList(struct ListNode* head){
|
||||
return reverse(NULL, head);
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
|
||||
双指针法:
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseList(head: ListNode): ListNode = {
|
||||
var pre: ListNode = null
|
||||
var cur = head
|
||||
while (cur != null) {
|
||||
var tmp = cur.next
|
||||
cur.next = pre
|
||||
pre = cur
|
||||
cur = tmp
|
||||
}
|
||||
pre
|
||||
}
|
||||
}
|
||||
```
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseList(head: ListNode): ListNode = {
|
||||
reverse(null, head)
|
||||
}
|
||||
|
||||
def reverse(pre: ListNode, cur: ListNode): ListNode = {
|
||||
if (cur == null) {
|
||||
return pre // 如果当前cur为空,则返回pre
|
||||
}
|
||||
val tmp: ListNode = cur.next
|
||||
cur.next = pre
|
||||
reverse(cur, tmp) // 此时cur成为前一个节点,tmp是当前节点
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -815,6 +815,89 @@ class MyStack {
|
||||
}
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
|
||||
使用两个队列模拟栈:
|
||||
```scala
|
||||
import scala.collection.mutable
|
||||
|
||||
class MyStack() {
|
||||
|
||||
val queue1 = new mutable.Queue[Int]()
|
||||
val queue2 = new mutable.Queue[Int]()
|
||||
|
||||
def push(x: Int) {
|
||||
queue1.enqueue(x)
|
||||
}
|
||||
|
||||
def pop(): Int = {
|
||||
var size = queue1.size
|
||||
// 将queue1中的每个元素都移动到queue2
|
||||
for (i <- 0 until size - 1) {
|
||||
queue2.enqueue(queue1.dequeue())
|
||||
}
|
||||
var res = queue1.dequeue()
|
||||
// 再将queue2中的每个元素都移动到queue1
|
||||
while (!queue2.isEmpty) {
|
||||
queue1.enqueue(queue2.dequeue())
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
def top(): Int = {
|
||||
var size = queue1.size
|
||||
for (i <- 0 until size - 1) {
|
||||
queue2.enqueue(queue1.dequeue())
|
||||
}
|
||||
var res = queue1.dequeue()
|
||||
while (!queue2.isEmpty) {
|
||||
queue1.enqueue(queue2.dequeue())
|
||||
}
|
||||
// 最终还需要把res送进queue1
|
||||
queue1.enqueue(res)
|
||||
res
|
||||
}
|
||||
|
||||
def empty(): Boolean = {
|
||||
queue1.isEmpty
|
||||
}
|
||||
}
|
||||
```
|
||||
使用一个队列模拟:
|
||||
```scala
|
||||
import scala.collection.mutable
|
||||
|
||||
class MyStack() {
|
||||
|
||||
val queue = new mutable.Queue[Int]()
|
||||
|
||||
def push(x: Int) {
|
||||
queue.enqueue(x)
|
||||
}
|
||||
|
||||
def pop(): Int = {
|
||||
var size = queue.size
|
||||
for (i <- 0 until size - 1) {
|
||||
queue.enqueue(queue.head) // 把头添到队列最后
|
||||
queue.dequeue() // 再出队
|
||||
}
|
||||
queue.dequeue()
|
||||
}
|
||||
|
||||
def top(): Int = {
|
||||
var size = queue.size
|
||||
var res = 0
|
||||
for (i <- 0 until size) {
|
||||
queue.enqueue(queue.head) // 把头添到队列最后
|
||||
res = queue.dequeue() // 再出队
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
def empty(): Boolean = {
|
||||
queue.isEmpty
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<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)
|
||||
|
@ -495,6 +495,45 @@ void myQueueFree(MyQueue* obj) {
|
||||
obj->stackOutTop = 0;
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
class MyQueue() {
|
||||
import scala.collection.mutable
|
||||
val stackIn = mutable.Stack[Int]() // 负责出栈
|
||||
val stackOut = mutable.Stack[Int]() // 负责入栈
|
||||
|
||||
// 添加元素
|
||||
def push(x: Int) {
|
||||
stackIn.push(x)
|
||||
}
|
||||
|
||||
// 复用代码,如果stackOut为空就把stackIn的所有元素都压入StackOut
|
||||
def dumpStackIn(): Unit = {
|
||||
if (!stackOut.isEmpty) return
|
||||
while (!stackIn.isEmpty) {
|
||||
stackOut.push(stackIn.pop())
|
||||
}
|
||||
}
|
||||
|
||||
// 弹出元素
|
||||
def pop(): Int = {
|
||||
dumpStackIn()
|
||||
stackOut.pop()
|
||||
}
|
||||
|
||||
// 获取队头
|
||||
def peek(): Int = {
|
||||
dumpStackIn()
|
||||
val res: Int = stackOut.pop()
|
||||
stackOut.push(res)
|
||||
res
|
||||
}
|
||||
|
||||
// 判断是否为空
|
||||
def empty(): Boolean = {
|
||||
stackIn.isEmpty && stackOut.isEmpty
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<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>
|
||||
|
@ -307,6 +307,52 @@ impl Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def isAnagram(s: String, t: String): Boolean = {
|
||||
// 如果两个字符串的长度不等,直接返回false
|
||||
if (s.length != t.length) return false
|
||||
val record = new Array[Int](26) // 记录每个单词出现了多少次
|
||||
// 遍历字符串,对于s字符串单词对应的记录+=1,t字符串对应的记录-=1
|
||||
for (i <- 0 until s.length) {
|
||||
record(s(i) - 97) += 1
|
||||
record(t(i) - 97) -= 1
|
||||
}
|
||||
// 如果不等于则直接返回false
|
||||
for (i <- 0 until 26) {
|
||||
if (record(i) != 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 如果前面不返回false,说明匹配成功,返回true,return可以省略
|
||||
true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public bool IsAnagram(string s, string t) {
|
||||
int sl=s.Length,tl=t.Length;
|
||||
if(sl!=tl) return false;
|
||||
int[] a = new int[26];
|
||||
for(int i = 0; i < sl; i++){
|
||||
a[s[i] - 'a']++;
|
||||
a[t[i] - 'a']--;
|
||||
}
|
||||
foreach (int i in a)
|
||||
{
|
||||
if (i != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## 相关题目
|
||||
|
||||
* 383.赎金信
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -325,6 +325,66 @@ const maxProfit = (prices) => {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 版本一,与本文思路一致
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
/**
|
||||
dp[i][0]: 持股状态;
|
||||
dp[i][1]: 无股状态,当天为非冷冻期;
|
||||
dp[i][2]: 无股状态,当天卖出;
|
||||
dp[i][3]: 无股状态,当天为冷冻期;
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
const dp: number[][] = new Array(length).fill(0).map(_ => []);
|
||||
dp[0][0] = -prices[0];
|
||||
dp[0][1] = dp[0][2] = dp[0][3] = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i][0] = Math.max(
|
||||
dp[i - 1][0],
|
||||
Math.max(dp[i - 1][1], dp[i - 1][3]) - prices[i]
|
||||
);
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]);
|
||||
dp[i][2] = dp[i - 1][0] + prices[i];
|
||||
dp[i][3] = dp[i - 1][2];
|
||||
}
|
||||
const lastEl: number[] = dp[length - 1];
|
||||
return Math.max(lastEl[1], lastEl[2], lastEl[3]);
|
||||
};
|
||||
```
|
||||
|
||||
> 版本二,状态定义略有不同,可以帮助理解
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[]): number {
|
||||
/**
|
||||
dp[i][0]: 持股状态,当天买入;
|
||||
dp[i][1]: 持股状态,当天未买入;
|
||||
dp[i][2]: 无股状态,当天卖出;
|
||||
dp[i][3]: 无股状态,当天未卖出;
|
||||
|
||||
买入有冷冻期限制,其实就是状态[0]只能由前一天的状态[3]得到;
|
||||
如果卖出有冷冻期限制,其实就是[2]由[1]得到。
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
const dp: number[][] = new Array(length).fill(0).map(_ => []);
|
||||
dp[0][0] = -prices[0];
|
||||
dp[0][1] = -Infinity;
|
||||
dp[0][2] = dp[0][3] = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i][0] = dp[i - 1][3] - prices[i];
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0]);
|
||||
dp[i][2] = Math.max(dp[i - 1][0], dp[i - 1][1]) + prices[i];
|
||||
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2]);
|
||||
}
|
||||
return Math.max(dp[length - 1][2], dp[length - 1][3]);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -429,7 +429,50 @@ const rob = root => {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
> 记忆化后序遍历
|
||||
|
||||
```typescript
|
||||
const memory: Map<TreeNode, number> = new Map();
|
||||
function rob(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
if (memory.has(root)) return memory.get(root);
|
||||
// 不取当前节点
|
||||
const res1: number = rob(root.left) + rob(root.right);
|
||||
// 取当前节点
|
||||
let res2: number = root.val;
|
||||
if (root.left !== null) res2 += rob(root.left.left) + rob(root.left.right);
|
||||
if (root.right !== null) res2 += rob(root.right.left) + rob(root.right.right);
|
||||
const res: number = Math.max(res1, res2);
|
||||
memory.set(root, res);
|
||||
return res;
|
||||
};
|
||||
```
|
||||
|
||||
> 状态标记化后序遍历
|
||||
|
||||
```typescript
|
||||
function rob(root: TreeNode | null): number {
|
||||
return Math.max(...robNode(root));
|
||||
};
|
||||
// [0]-不偷当前节点能获得的最大金额; [1]-偷~~
|
||||
type MaxValueArr = [number, number];
|
||||
function robNode(node: TreeNode | null): MaxValueArr {
|
||||
if (node === null) return [0, 0];
|
||||
const leftArr: MaxValueArr = robNode(node.left);
|
||||
const rightArr: MaxValueArr = robNode(node.right);
|
||||
// 不偷
|
||||
const val1: number = Math.max(leftArr[0], leftArr[1]) +
|
||||
Math.max(rightArr[0], rightArr[1]);
|
||||
// 偷
|
||||
const val2: number = leftArr[0] + rightArr[0] + node.val;
|
||||
return [val1, val2];
|
||||
}
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
// 打家劫舍Ⅲ 动态规划
|
||||
// 时间复杂度O(n) 空间复杂度O(logn)
|
||||
|
@ -266,6 +266,20 @@ public class Solution
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseString(s: Array[Char]): Unit = {
|
||||
var (left, right) = (0, s.length - 1)
|
||||
while (left < right) {
|
||||
var tmp = s(left)
|
||||
s(left) = s(right)
|
||||
s(right) = tmp
|
||||
left += 1
|
||||
right -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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>
|
||||
|
@ -313,6 +313,69 @@ int* intersection1(int* nums1, int nums1Size, int* nums2, int nums2Size, int* re
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
正常解法:
|
||||
```scala
|
||||
object Solution {
|
||||
def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = {
|
||||
// 导入mutable
|
||||
import scala.collection.mutable
|
||||
// 临时Set,用于记录数组1出现的每个元素
|
||||
val tmpSet: mutable.HashSet[Int] = new mutable.HashSet[Int]()
|
||||
// 结果Set,存储最终结果
|
||||
val resSet: mutable.HashSet[Int] = new mutable.HashSet[Int]()
|
||||
// 遍历nums1,把每个元素添加到tmpSet
|
||||
nums1.foreach(tmpSet.add(_))
|
||||
// 遍历nums2,如果在tmpSet存在就添加到resSet
|
||||
nums2.foreach(elem => {
|
||||
if (tmpSet.contains(elem)) {
|
||||
resSet.add(elem)
|
||||
}
|
||||
})
|
||||
// 将结果转换为Array返回,return可以省略
|
||||
resSet.toArray
|
||||
}
|
||||
}
|
||||
```
|
||||
骚操作1:
|
||||
```scala
|
||||
object Solution {
|
||||
def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = {
|
||||
// 先转为Set,然后取交集,最后转换为Array
|
||||
(nums1.toSet).intersect(nums2.toSet).toArray
|
||||
}
|
||||
}
|
||||
```
|
||||
骚操作2:
|
||||
```scala
|
||||
object Solution {
|
||||
def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = {
|
||||
// distinct去重,然后取交集
|
||||
(nums1.distinct).intersect(nums2.distinct)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public int[] Intersection(int[] nums1, int[] nums2) {
|
||||
if(nums1==null||nums1.Length==0||nums2==null||nums1.Length==0)
|
||||
return new int[0]; //注意数组条件
|
||||
HashSet<int> one = Insert(nums1);
|
||||
HashSet<int> two = Insert(nums2);
|
||||
one.IntersectWith(two);
|
||||
return one.ToArray();
|
||||
}
|
||||
public HashSet<int> Insert(int[] nums){
|
||||
HashSet<int> one = new HashSet<int>();
|
||||
foreach(int num in nums){
|
||||
one.Add(num);
|
||||
}
|
||||
return one;
|
||||
}
|
||||
|
||||
```
|
||||
## 相关题目
|
||||
|
||||
* 350.两个数组的交集 II
|
||||
|
@ -363,5 +363,87 @@ impl Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
版本一: 使用数组作为哈希表
|
||||
```scala
|
||||
object Solution {
|
||||
def canConstruct(ransomNote: String, magazine: String): Boolean = {
|
||||
// 如果magazine的长度小于ransomNote的长度,必然是false
|
||||
if (magazine.length < ransomNote.length) {
|
||||
return false
|
||||
}
|
||||
// 定义一个数组,存储magazine字符出现的次数
|
||||
val map: Array[Int] = new Array[Int](26)
|
||||
// 遍历magazine字符串,对应的字符+=1
|
||||
for (i <- magazine.indices) {
|
||||
map(magazine(i) - 'a') += 1
|
||||
}
|
||||
// 遍历ransomNote
|
||||
for (i <- ransomNote.indices) {
|
||||
if (map(ransomNote(i) - 'a') > 0)
|
||||
map(ransomNote(i) - 'a') -= 1
|
||||
else return false
|
||||
}
|
||||
// 如果上面没有返回false,直接返回true,关键字return可以省略
|
||||
true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def canConstruct(ransomNote: String, magazine: String): Boolean = {
|
||||
// 如果magazine的长度小于ransomNote的长度,必然是false
|
||||
if (magazine.length < ransomNote.length) {
|
||||
return false
|
||||
}
|
||||
// 定义map,key是字符,value是字符出现的次数
|
||||
val map = new mutable.HashMap[Char, Int]()
|
||||
// 遍历magazine,把所有的字符都记录到map里面
|
||||
for (i <- magazine.indices) {
|
||||
val tmpChar = magazine(i)
|
||||
// 如果map包含该字符,那么对应的value++,否则添加该字符
|
||||
if (map.contains(tmpChar)) {
|
||||
map.put(tmpChar, map.get(tmpChar).get + 1)
|
||||
} else {
|
||||
map.put(tmpChar, 1)
|
||||
}
|
||||
}
|
||||
// 遍历ransomNote
|
||||
for (i <- ransomNote.indices) {
|
||||
val tmpChar = ransomNote(i)
|
||||
// 如果map包含并且该字符的value大于0,则匹配成功,map对应的--,否则直接返回false
|
||||
if (map.contains(tmpChar) && map.get(tmpChar).get > 0) {
|
||||
map.put(tmpChar, map.get(tmpChar).get - 1)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 如果上面没有返回false,直接返回true,关键字return可以省略
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public bool CanConstruct(string ransomNote, string magazine) {
|
||||
if(ransomNote.Length > magazine.Length) return false;
|
||||
int[] letters = new int[26];
|
||||
foreach(char c in magazine){
|
||||
letters[c-'a']++;
|
||||
}
|
||||
foreach(char c in ransomNote){
|
||||
letters[c-'a']--;
|
||||
if(letters[c-'a']<0){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -336,8 +336,8 @@ var sumOfLeftLeaves = function(root) {
|
||||
if(node===null){
|
||||
return 0;
|
||||
}
|
||||
let leftValue = sumOfLeftLeaves(node.left);
|
||||
let rightValue = sumOfLeftLeaves(node.right);
|
||||
let leftValue = nodesSum(node.left);
|
||||
let rightValue = nodesSum(node.right);
|
||||
// 3. 单层递归逻辑
|
||||
let midValue = 0;
|
||||
if(node.left&&node.left.left===null&&node.left.right===null){
|
||||
|
@ -318,5 +318,69 @@ impl Solution {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
// 导包
|
||||
import scala.collection.mutable
|
||||
def fourSumCount(nums1: Array[Int], nums2: Array[Int], nums3: Array[Int], nums4: Array[Int]): Int = {
|
||||
// 定义一个HashMap,key存储值,value存储该值出现的次数
|
||||
val map = new mutable.HashMap[Int, Int]()
|
||||
// 遍历前两个数组,把他们所有可能的情况都记录到map
|
||||
for (i <- nums1.indices) {
|
||||
for (j <- nums2.indices) {
|
||||
val tmp = nums1(i) + nums2(j)
|
||||
// 如果包含该值,则对他的key加1,不包含则添加进去
|
||||
if (map.contains(tmp)) {
|
||||
map.put(tmp, map.get(tmp).get + 1)
|
||||
} else {
|
||||
map.put(tmp, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
var res = 0 // 结果变量
|
||||
// 遍历后两个数组
|
||||
for (i <- nums3.indices) {
|
||||
for (j <- nums4.indices) {
|
||||
val tmp = -(nums3(i) + nums4(j))
|
||||
// 如果map中存在该值,结果就+=value
|
||||
if (map.contains(tmp)) {
|
||||
res += map.get(tmp).get
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回最终结果,可以省略关键字return
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public int FourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
|
||||
Dictionary<int, int> dic = new Dictionary<int, int>();
|
||||
foreach(var i in nums1){
|
||||
foreach(var j in nums2){
|
||||
int sum = i + j;
|
||||
if(dic.ContainsKey(sum)){
|
||||
dic[sum]++;
|
||||
}else{
|
||||
dic.Add(sum, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
int res = 0;
|
||||
foreach(var a in nums3){
|
||||
foreach(var b in nums4){
|
||||
int sum = a+b;
|
||||
if(dic.TryGetValue(-sum, out var result)){
|
||||
res += result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -186,29 +186,28 @@ class Solution:
|
||||
Go:
|
||||
```Go
|
||||
func longestPalindromeSubseq(s string) int {
|
||||
lenth:=len(s)
|
||||
dp:=make([][]int,lenth)
|
||||
for i:=0;i<lenth;i++{
|
||||
for j:=0;j<lenth;j++{
|
||||
if dp[i]==nil{
|
||||
dp[i]=make([]int,lenth)
|
||||
}
|
||||
if i==j{
|
||||
dp[i][j]=1
|
||||
size := len(s)
|
||||
max := func(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
dp := make([][]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
dp[i] = make([]int, size)
|
||||
dp[i][i] = 1
|
||||
}
|
||||
for i := size - 1; i >= 0; i-- {
|
||||
for j := i + 1; j < size; j++ {
|
||||
if s[i] == s[j] {
|
||||
dp[i][j] = dp[i+1][j-1] + 2
|
||||
} else {
|
||||
dp[i][j] = max(dp[i][j-1], dp[i+1][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i:=lenth-1;i>=0;i--{
|
||||
for j:=i+1;j<lenth;j++{
|
||||
if s[i]==s[j]{
|
||||
dp[i][j]=dp[i+1][j-1]+2
|
||||
}else {
|
||||
dp[i][j]=max(dp[i+1][j],dp[i][j-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dp[0][lenth-1]
|
||||
return dp[0][size-1]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -347,6 +347,47 @@ public class Solution
|
||||
}
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
|
||||
版本一: (正常解法)
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseStr(s: String, k: Int): String = {
|
||||
val res = s.toCharArray // 转换为Array好处理
|
||||
for (i <- s.indices by 2 * k) {
|
||||
// 如果i+k大于了res的长度,则需要全部翻转
|
||||
if (i + k > res.length) {
|
||||
reverse(res, i, s.length - 1)
|
||||
} else {
|
||||
reverse(res, i, i + k - 1)
|
||||
}
|
||||
}
|
||||
new String(res)
|
||||
}
|
||||
// 翻转字符串,从start到end
|
||||
def reverse(s: Array[Char], start: Int, end: Int): Unit = {
|
||||
var (left, right) = (start, end)
|
||||
while (left < right) {
|
||||
var tmp = s(left)
|
||||
s(left) = s(right)
|
||||
s(right) = tmp
|
||||
left += 1
|
||||
right -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
版本二: 首先利用sliding每隔k个进行分割,随后转换为数组,再使用zipWithIndex添加每个数组的索引,紧接着利用map做变换,如果索引%2==0则说明需要翻转,否则原封不动,最后再转换为String
|
||||
```scala
|
||||
object Solution {
|
||||
def reverseStr(s: String, k: Int): String = {
|
||||
s.sliding(k, k)
|
||||
.toArray
|
||||
.zipWithIndex
|
||||
.map(v => if (v._2 % 2 == 0) v._1.reverse else v._1)
|
||||
.mkString
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<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`呢?
|
||||
@ -611,6 +613,8 @@ public class Solution{
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**Kotlin:**
|
||||
```Kotlin
|
||||
// (版本一)左闭右开区间
|
||||
@ -642,6 +646,48 @@ class Solution {
|
||||
}
|
||||
}
|
||||
```
|
||||
**Scala:**
|
||||
|
||||
(版本一)左闭右闭区间
|
||||
```scala
|
||||
object Solution {
|
||||
def search(nums: Array[Int], target: Int): Int = {
|
||||
var left = 0
|
||||
var right = nums.length - 1
|
||||
while (left <= right) {
|
||||
var mid = left + ((right - left) / 2)
|
||||
if (target == nums(mid)) {
|
||||
return mid
|
||||
} else if (target < nums(mid)) {
|
||||
right = mid - 1
|
||||
} else {
|
||||
left = mid + 1
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
}
|
||||
```
|
||||
(版本二)左闭右开区间
|
||||
```scala
|
||||
object Solution {
|
||||
def search(nums: Array[Int], target: Int): Int = {
|
||||
var left = 0
|
||||
var right = nums.length
|
||||
while (left < right) {
|
||||
val mid = left + (right - left) / 2
|
||||
if (target == nums(mid)) {
|
||||
return mid
|
||||
} else if (target < nums(mid)) {
|
||||
right = mid
|
||||
} else {
|
||||
left = mid + 1
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -1154,7 +1154,75 @@ class MyLinkedList {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
class ListNode(_x: Int = 0, _next: ListNode = null) {
|
||||
var next: ListNode = _next
|
||||
var x: Int = _x
|
||||
}
|
||||
|
||||
class MyLinkedList() {
|
||||
|
||||
var size = 0 // 链表尺寸
|
||||
var dummy: ListNode = new ListNode(0) // 虚拟头节点
|
||||
|
||||
// 获取第index个节点的值
|
||||
def get(index: Int): Int = {
|
||||
if (index < 0 || index >= size) {
|
||||
return -1;
|
||||
}
|
||||
var cur = dummy
|
||||
for (i <- 0 to index) {
|
||||
cur = cur.next
|
||||
}
|
||||
cur.x // 返回cur的值
|
||||
}
|
||||
|
||||
// 在链表最前面插入一个节点
|
||||
def addAtHead(`val`: Int) {
|
||||
addAtIndex(0, `val`)
|
||||
}
|
||||
|
||||
// 在链表最后面插入一个节点
|
||||
def addAtTail(`val`: Int) {
|
||||
addAtIndex(size, `val`)
|
||||
}
|
||||
|
||||
// 在第index个节点之前插入一个新节点
|
||||
// 如果index等于链表长度,则说明新插入的节点是尾巴
|
||||
// 如果index等于0,则说明新插入的节点是头
|
||||
// 如果index>链表长度,则说明为空
|
||||
def addAtIndex(index: Int, `val`: Int) {
|
||||
if (index > size) {
|
||||
return
|
||||
}
|
||||
var loc = index // 因为参数index是val不可变类型,所以需要赋值给一个可变类型
|
||||
if (index < 0) {
|
||||
loc = 0
|
||||
}
|
||||
size += 1 //链表尺寸+1
|
||||
var pre = dummy
|
||||
for (i <- 0 until loc) {
|
||||
pre = pre.next
|
||||
}
|
||||
val node: ListNode = new ListNode(`val`, pre.next)
|
||||
pre.next = node
|
||||
}
|
||||
// 删除第index个节点
|
||||
def deleteAtIndex(index: Int) {
|
||||
if (index < 0 || index >= size) {
|
||||
return
|
||||
}
|
||||
size -= 1
|
||||
var pre = dummy
|
||||
for (i <- 0 until index) {
|
||||
pre = pre.next
|
||||
}
|
||||
pre.next = pre.next.next
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -200,6 +200,29 @@ const maxProfit = (prices,fee) => {
|
||||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[], fee: number): number {
|
||||
/**
|
||||
dp[i][0]:持有股票
|
||||
dp[i][1]: 不持有
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
if (length === 0) return 0;
|
||||
const dp: number[][] = new Array(length).fill(0).map(_ => []);
|
||||
dp[0][0] = -prices[0];
|
||||
dp[0][1] = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
|
||||
}
|
||||
return dp[length - 1][1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<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>
|
||||
|
@ -34,7 +34,8 @@
|
||||
|
||||
那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?
|
||||
|
||||
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素大的元素,优点是只需要遍历一次。
|
||||
|
||||
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。
|
||||
|
||||
|
||||
在使用单调栈的时候首先要明确如下几点:
|
||||
@ -192,7 +193,7 @@ class Solution {
|
||||
否则的话,可以直接入栈。
|
||||
注意,单调栈里 加入的元素是 下标。
|
||||
*/
|
||||
Stack<Integer>stack=new Stack<>();
|
||||
Deque<Integer> stack=new LinkedList<>();
|
||||
stack.push(0);
|
||||
for(int i=1;i<lens;i++){
|
||||
|
||||
@ -216,7 +217,7 @@ class Solution {
|
||||
public int[] dailyTemperatures(int[] temperatures) {
|
||||
int lens=temperatures.length;
|
||||
int []res=new int[lens];
|
||||
Stack<Integer>stack=new Stack<>();
|
||||
Deque<Integer> stack=new LinkedList<>();
|
||||
for(int i=0;i<lens;i++){
|
||||
|
||||
while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
# 思路
|
||||
|
||||
为了易于大家理解,我还特意录制了视频,[本题视频讲解](https://www.bilibili.com/video/BV1QB4y1D7ep)
|
||||
|
||||
## 暴力排序
|
||||
|
||||
最直观的想法,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
|
||||
|
@ -418,6 +418,38 @@ char ** commonChars(char ** words, int wordsSize, int* returnSize){
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def commonChars(words: Array[String]): List[String] = {
|
||||
// 声明返回结果的不可变List集合,因为res要重新赋值,所以声明为var
|
||||
var res = List[String]()
|
||||
var hash = new Array[Int](26) // 统计字符出现的最小频率
|
||||
// 统计第一个字符串中字符出现的次数
|
||||
for (i <- 0 until words(0).length) {
|
||||
hash(words(0)(i) - 'a') += 1
|
||||
}
|
||||
// 统计其他字符串出现的频率
|
||||
for (i <- 1 until words.length) {
|
||||
// 统计其他字符出现的频率
|
||||
var hashOtherStr = new Array[Int](26)
|
||||
for (j <- 0 until words(i).length) {
|
||||
hashOtherStr(words(i)(j) - 'a') += 1
|
||||
}
|
||||
// 更新hash,取26个字母最小出现的频率
|
||||
for (k <- 0 until 26) {
|
||||
hash(k) = math.min(hash(k), hashOtherStr(k))
|
||||
}
|
||||
}
|
||||
// 根据hash的结果转换输出的形式
|
||||
for (i <- 0 until 26) {
|
||||
for (j <- 0 until hash(i)) {
|
||||
res = res :+ (i + 'a').toChar.toString
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -374,6 +374,27 @@ func removeDuplicates(_ s: String) -> String {
|
||||
return String(stack)
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def removeDuplicates(s: String): String = {
|
||||
var stack = mutable.Stack[Int]()
|
||||
var str = "" // 保存最终结果
|
||||
for (i <- s.indices) {
|
||||
var tmp = s(i)
|
||||
// 如果栈非空并且栈顶元素等于当前字符,那么删掉栈顶和字符串最后一个元素
|
||||
if (!stack.isEmpty && tmp == stack.head) {
|
||||
str = str.take(str.length - 1)
|
||||
stack.pop()
|
||||
} else {
|
||||
stack.push(tmp)
|
||||
str += tmp
|
||||
}
|
||||
}
|
||||
str
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
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!**
|
||||
|
126
problems/qita/server.md
Normal file
126
problems/qita/server.md
Normal file
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -413,8 +413,62 @@ func replaceSpace(_ s: String) -> String {
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
|
||||
方式一: 双指针
|
||||
```scala
|
||||
object Solution {
|
||||
def replaceSpace(s: String): String = {
|
||||
var count = 0
|
||||
s.foreach(c => if (c == ' ') count += 1) // 统计空格的数量
|
||||
val sOldSize = s.length // 旧数组字符串长度
|
||||
val sNewSize = s.length + count * 2 // 新数组字符串长度
|
||||
val res = new Array[Char](sNewSize) // 新数组
|
||||
var index = sNewSize - 1 // 新数组索引
|
||||
// 逆序遍历
|
||||
for (i <- (0 until sOldSize).reverse) {
|
||||
if (s(i) == ' ') {
|
||||
res(index) = '0'
|
||||
index -= 1
|
||||
res(index) = '2'
|
||||
index -= 1
|
||||
res(index) = '%'
|
||||
} else {
|
||||
res(index) = s(i)
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
res.mkString
|
||||
}
|
||||
}
|
||||
```
|
||||
方式二: 使用一个集合,遇到空格就添加%20
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def replaceSpace(s: String): String = {
|
||||
val res: ListBuffer[Char] = ListBuffer[Char]()
|
||||
for (i <- s.indices) {
|
||||
if (s(i) == ' ') {
|
||||
res += '%'
|
||||
res += '2'
|
||||
res += '0'
|
||||
}else{
|
||||
res += s(i)
|
||||
}
|
||||
}
|
||||
res.mkString
|
||||
}
|
||||
}
|
||||
```
|
||||
方式三: 使用map
|
||||
```scala
|
||||
object Solution {
|
||||
def replaceSpace(s: String): String = {
|
||||
s.map(c => if(c == ' ') "%20" else c).mkString
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -291,6 +291,56 @@ func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) {
|
||||
```
|
||||
|
||||
|
||||
### PHP
|
||||
|
||||
```php
|
||||
function reverseLeftWords($s, $n) {
|
||||
$this->reverse($s,0,$n-1); //反转区间为前n的子串
|
||||
$this->reverse($s,$n,strlen($s)-1); //反转区间为n到末尾的子串
|
||||
$this->reverse($s,0,strlen($s)-1); //反转整个字符串
|
||||
return $s;
|
||||
}
|
||||
|
||||
// 按指定进行翻转 【array、string都可】
|
||||
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 {
|
||||
def reverseLeftWords(s: String, n: Int): String = {
|
||||
var str = s.toCharArray // 转换为Array
|
||||
// abcdefg => ba cdefg
|
||||
reverseString(str, 0, n - 1)
|
||||
// ba cdefg => ba gfedc
|
||||
reverseString(str, n, str.length - 1)
|
||||
// ba gfedc => cdefgab
|
||||
reverseString(str, 0, str.length - 1)
|
||||
// 最终返回,return关键字可以省略
|
||||
new String(str)
|
||||
}
|
||||
// 翻转字符串
|
||||
def reverseString(s: Array[Char], start: Int, end: Int): Unit = {
|
||||
var (left, right) = (start, end)
|
||||
while (left < right) {
|
||||
var tmp = s(left)
|
||||
s(left) = s(right)
|
||||
s(right) = tmp
|
||||
left += 1
|
||||
right -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -380,53 +380,31 @@ func main() {
|
||||
### javascript
|
||||
|
||||
```js
|
||||
/**
|
||||
*
|
||||
* @param {Number []} weight
|
||||
* @param {Number []} value
|
||||
* @param {Number} size
|
||||
* @returns
|
||||
*/
|
||||
function testWeightBagProblem (weight, value, size) {
|
||||
// 定义 dp 数组
|
||||
const len = weight.length,
|
||||
dp = Array(len).fill().map(() => Array(size + 1).fill(0));
|
||||
|
||||
function testWeightBagProblem(weight, value, size) {
|
||||
const len = weight.length,
|
||||
dp = Array.from({length: len}).map(
|
||||
() => Array(size + 1)) //JavaScript 数组是引用类型
|
||||
for(let i = 0; i < len; i++) { //初始化最左一列,即背包容量为0时的情况
|
||||
dp[i][0] = 0;
|
||||
}
|
||||
for(let j = 1; j < size+1; j++) { //初始化第0行, 只有一件物品的情况
|
||||
if(weight[0] <= j) {
|
||||
dp[0][j] = value[0];
|
||||
} else {
|
||||
dp[0][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 1; i < len; i++) { //dp[i][j]由其左上方元素推导得出
|
||||
for(let j = 1; j < size+1; j++) {
|
||||
if(j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return dp[len-1][size] //满足条件的最大值
|
||||
}
|
||||
|
||||
function testWeightBagProblem2 (wight, value, size) {
|
||||
const len = wight.length,
|
||||
dp = Array(size + 1).fill(0);
|
||||
for(let i = 1; i <= len; i++) {
|
||||
for(let j = size; j >= wight[i - 1]; j--) {
|
||||
dp[j] = Math.max(dp[j], value[i - 1] + dp[j - wight[i - 1]]);
|
||||
// 初始化
|
||||
for(let j = weight[0]; j <= size; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
}
|
||||
return dp[size];
|
||||
}
|
||||
|
||||
// weight 数组的长度len 就是物品个数
|
||||
for(let i = 1; i < len; i++) { // 遍历物品
|
||||
for(let j = 0; j <= size; j++) { // 遍历背包容量
|
||||
if(j < weight[i]) dp[i][j] = dp[i - 1][j];
|
||||
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
console.table(dp)
|
||||
|
||||
return dp[len - 1][size];
|
||||
}
|
||||
|
||||
function test () {
|
||||
console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6));
|
||||
console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6));
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -317,7 +317,55 @@ ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def getIntersectionNode(headA: ListNode, headB: ListNode): ListNode = {
|
||||
var lenA = 0 // headA链表的长度
|
||||
var lenB = 0 // headB链表的长度
|
||||
var tmp = headA // 临时变量
|
||||
// 统计headA的长度
|
||||
while (tmp != null) {
|
||||
lenA += 1;
|
||||
tmp = tmp.next
|
||||
}
|
||||
// 统计headB的长度
|
||||
tmp = headB // 临时变量赋值给headB
|
||||
while (tmp != null) {
|
||||
lenB += 1
|
||||
tmp = tmp.next
|
||||
}
|
||||
// 因为传递过来的参数是不可变量,所以需要重新定义
|
||||
var listA = headA
|
||||
var listB = headB
|
||||
// 两个链表的长度差
|
||||
// 如果gap>0,lenA>lenB,headA(listA)链表往前移动gap步
|
||||
// 如果gap<0,lenA<lenB,headB(listB)链表往前移动-gap步
|
||||
var gap = lenA - lenB
|
||||
if (gap > 0) {
|
||||
// 因为不可以i-=1,所以可以使用for
|
||||
for (i <- 0 until gap) {
|
||||
listA = listA.next // 链表headA(listA) 移动
|
||||
}
|
||||
} else {
|
||||
gap = math.abs(gap) // 此刻gap为负值,取绝对值
|
||||
for (i <- 0 until gap) {
|
||||
listB = listB.next
|
||||
}
|
||||
}
|
||||
// 现在两个链表同时往前走,如果相等则返回
|
||||
while (listA != null && listB != null) {
|
||||
if (listA == listB) {
|
||||
return listA
|
||||
}
|
||||
listA = listA.next
|
||||
listB = listB.next
|
||||
}
|
||||
// 如果链表没有相交则返回null,return可以省略
|
||||
null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
Reference in New Issue
Block a user