Merge branch 'master' of https://github.com/KingArthur0205/leetcode-master
This commit is contained in:
commit
ed3f6a0cde
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
分为如下几步:
|
||||
|
||||
* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
|
||||
* 首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
|
||||
|
||||
* 定义fast指针和slow指针,初始值为虚拟头结点,如图:
|
||||
|
||||
|
|
@ -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,13 +77,23 @@ public:
|
|||
|
||||
双指针法(快慢指针法): **通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。**
|
||||
|
||||
定义快慢指针
|
||||
|
||||
* 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
|
||||
* 慢指针:指向更新 新数组下标的位置
|
||||
|
||||
很多同学这道题目做的很懵,就是不理解 快慢指针究竟都是什么含义,所以一定要明确含义,后面的思路就更容易理解了。
|
||||
|
||||
删除过程如下:
|
||||
|
||||

|
||||
|
||||
很多同学不了解
|
||||
|
||||
|
||||
**双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。**
|
||||
|
||||
后序都会一一介绍到,本题代码如下:
|
||||
后续都会一一介绍到,本题代码如下:
|
||||
|
||||
```CPP
|
||||
// 时间复杂度:O(n)
|
||||
|
|
@ -104,8 +116,6 @@ public:
|
|||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
|
||||
|
||||
```CPP
|
||||
/**
|
||||
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
|
||||
|
|
@ -329,5 +339,34 @@ int removeElement(int* nums, int numsSize, int val){
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
fun removeElement(nums: IntArray, `val`: Int): Int {
|
||||
var slowIndex = 0 // 初始化慢指针
|
||||
for (fastIndex in nums.indices) {
|
||||
if (nums[fastIndex] != `val`) nums[slowIndex++] = nums[fastIndex] // 在慢指针所在位置存储未被删除的元素
|
||||
}
|
||||
return slowIndex
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
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,8 +316,52 @@ 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
|
||||
|
||||
```php
|
||||
// 二分法(1):[左闭右闭]
|
||||
function searchInsert($nums, $target)
|
||||
{
|
||||
$n = count($nums);
|
||||
$l = 0;
|
||||
$r = $n - 1;
|
||||
while ($l <= $r) {
|
||||
$mid = floor(($l + $r) / 2);
|
||||
if ($nums[$mid] > $target) {
|
||||
// 下次搜索在左区间:[$l,$mid-1]
|
||||
$r = $mid - 1;
|
||||
} else if ($nums[$mid] < $target) {
|
||||
// 下次搜索在右区间:[$mid+1,$r]
|
||||
$l = $mid + 1;
|
||||
} else {
|
||||
// 命中返回
|
||||
return $mid;
|
||||
}
|
||||
}
|
||||
return $r + 1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -370,18 +370,17 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
|
|||
```js
|
||||
var combinationSum = function(candidates, target) {
|
||||
const res = [], path = [];
|
||||
candidates.sort(); // 排序
|
||||
candidates.sort((a,b)=>a-b); // 排序
|
||||
backtracking(0, 0);
|
||||
return res;
|
||||
function backtracking(j, sum) {
|
||||
if (sum > target) return;
|
||||
if (sum === target) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
for(let i = j; i < candidates.length; i++ ) {
|
||||
const n = candidates[i];
|
||||
if(n > target - sum) continue;
|
||||
if(n > target - sum) break;
|
||||
path.push(n);
|
||||
sum += n;
|
||||
backtracking(i, sum);
|
||||
|
|
|
|||
|
|
@ -508,22 +508,27 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
|
|||
*/
|
||||
var combinationSum2 = function(candidates, target) {
|
||||
const res = []; path = [], len = candidates.length;
|
||||
candidates.sort();
|
||||
candidates.sort((a,b)=>a-b);
|
||||
backtracking(0, 0);
|
||||
return res;
|
||||
function backtracking(sum, i) {
|
||||
if (sum > target) return;
|
||||
if (sum === target) {
|
||||
res.push(Array.from(path));
|
||||
return;
|
||||
}
|
||||
let f = -1;
|
||||
for(let j = i; j < len; j++) {
|
||||
const n = candidates[j];
|
||||
if(n > target - sum || n === f) continue;
|
||||
if(j > i && candidates[j] === candidates[j-1]){
|
||||
//若当前元素和前一个元素相等
|
||||
//则本次循环结束,防止出现重复组合
|
||||
continue;
|
||||
}
|
||||
//如果当前元素值大于目标值-总和的值
|
||||
//由于数组已排序,那么该元素之后的元素必定不满足条件
|
||||
//直接终止当前层的递归
|
||||
if(n > target - sum) break;
|
||||
path.push(n);
|
||||
sum += n;
|
||||
f = n;
|
||||
backtracking(sum, j + 1);
|
||||
path.pop();
|
||||
sum -= n;
|
||||
|
|
|
|||
|
|
@ -744,6 +744,91 @@ var trap = function(height) {
|
|||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
双指针法:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
let resVal: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let leftMaxHeight: number = height[i],
|
||||
rightMaxHeight: number = height[i];
|
||||
let leftIndex: number = i - 1,
|
||||
rightIndex: number = i + 1;
|
||||
while (leftIndex >= 0) {
|
||||
if (height[leftIndex] > leftMaxHeight)
|
||||
leftMaxHeight = height[leftIndex];
|
||||
leftIndex--;
|
||||
}
|
||||
while (rightIndex < length) {
|
||||
if (height[rightIndex] > rightMaxHeight)
|
||||
rightMaxHeight = height[rightIndex];
|
||||
rightIndex++;
|
||||
}
|
||||
resVal += Math.min(leftMaxHeight, rightMaxHeight) - height[i];
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
动态规划:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
const leftMaxHeightDp: number[] = [],
|
||||
rightMaxHeightDp: number[] = [];
|
||||
leftMaxHeightDp[0] = height[0];
|
||||
rightMaxHeightDp[length - 1] = height[length - 1];
|
||||
for (let i = 1; i < length; i++) {
|
||||
leftMaxHeightDp[i] = Math.max(height[i], leftMaxHeightDp[i - 1]);
|
||||
}
|
||||
for (let i = length - 2; i >= 0; i--) {
|
||||
rightMaxHeightDp[i] = Math.max(height[i], rightMaxHeightDp[i + 1]);
|
||||
}
|
||||
let resVal: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
resVal += Math.min(leftMaxHeightDp[i], rightMaxHeightDp[i]) - height[i];
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
单调栈:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
const length: number = height.length;
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
let resVal: number = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
if (height[top] > height[i]) {
|
||||
stack.push(i);
|
||||
} else if (height[top] === height[i]) {
|
||||
stack.pop();
|
||||
stack.push(i);
|
||||
} else {
|
||||
while (stack.length > 0 && height[top] < height[i]) {
|
||||
let mid = stack.pop();
|
||||
if (stack.length > 0) {
|
||||
let left = stack[stack.length - 1];
|
||||
let h = Math.min(height[left], height[i]) - height[mid];
|
||||
let w = i - left - 1;
|
||||
resVal += h * w;
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resVal;
|
||||
};
|
||||
```
|
||||
|
||||
### C:
|
||||
|
||||
一种更简便的双指针方法:
|
||||
|
|
|
|||
|
|
@ -217,18 +217,26 @@ class Solution:
|
|||
### Go
|
||||
```Go
|
||||
func jump(nums []int) int {
|
||||
dp:=make([]int ,len(nums))
|
||||
dp[0]=0
|
||||
dp := make([]int, len(nums))
|
||||
dp[0] = 0//初始第一格跳跃数一定为0
|
||||
|
||||
for i:=1;i<len(nums);i++{
|
||||
dp[i]=i
|
||||
for j:=0;j<i;j++{
|
||||
if nums[j]+j>i{
|
||||
dp[i]=min(dp[j]+1,dp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(nums)-1]
|
||||
for i := 1; i < len(nums); i++ {
|
||||
dp[i] = i
|
||||
for j := 0; j < i; j++ {
|
||||
if nums[j] + j >= i {//nums[j]为起点,j为往右跳的覆盖范围,这行表示从j能跳到i
|
||||
dp[i] = min(dp[j] + 1, dp[i])//更新最小能到i的跳跃次数
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(nums)-1]
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ function permute(nums: number[]): number[][] {
|
|||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
let tempVal: number;
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ var permuteUnique = function (nums) {
|
|||
|
||||
function backtracing( used) {
|
||||
if (path.length === nums.length) {
|
||||
result.push(path.slice())
|
||||
result.push([...path])
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
|
|
@ -303,7 +303,7 @@ function permuteUnique(nums: number[]): number[][] {
|
|||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
return;
|
||||
}
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
|
|
|
|||
|
|
@ -212,6 +212,20 @@ func maxSubArray(nums []int) int {
|
|||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
pub fn max_sub_array(nums: Vec<i32>) -> i32 {
|
||||
let mut max_sum = i32::MIN;
|
||||
let mut curr = 0;
|
||||
for n in nums.iter() {
|
||||
curr += n;
|
||||
max_sum = max_sum.max(curr);
|
||||
curr = curr.max(0);
|
||||
}
|
||||
max_sum
|
||||
}
|
||||
```
|
||||
|
||||
### Javascript:
|
||||
```Javascript
|
||||
var maxSubArray = function(nums) {
|
||||
|
|
|
|||
|
|
@ -186,6 +186,24 @@ const maxSubArray = nums => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxSubArray(nums: number[]): number {
|
||||
/**
|
||||
dp[i]:以nums[i]结尾的最大和
|
||||
*/
|
||||
const dp: number[] = []
|
||||
dp[0] = nums[0];
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i < nums.length; i++) {
|
||||
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
|
||||
resMax = Math.max(resMax, dp[i]);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
@ -112,8 +112,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n\log n)$ ,有一个快排
|
||||
* 空间复杂度:$O(1)$,我没有算result数组(返回值所需容器占的空间)
|
||||
* 时间复杂度:O(nlog n) ,有一个快排
|
||||
* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
|
||||
## 总结
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -564,6 +544,57 @@ int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
|
|||
return ans;
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
```scala
|
||||
object Solution {
|
||||
def generateMatrix(n: Int): Array[Array[Int]] = {
|
||||
var res = Array.ofDim[Int](n, n) // 定义一个n*n的二维矩阵
|
||||
var num = 1 // 标志当前到了哪个数字
|
||||
var i = 0 // 横坐标
|
||||
var j = 0 // 竖坐标
|
||||
|
||||
while (num <= n * n) {
|
||||
// 向右:当j不越界,并且下一个要填的数字是空白时
|
||||
while (j < n && res(i)(j) == 0) {
|
||||
res(i)(j) = num // 当前坐标等于num
|
||||
num += 1 // num++
|
||||
j += 1 // 竖坐标+1
|
||||
}
|
||||
i += 1 // 下移一行
|
||||
j -= 1 // 左移一列
|
||||
|
||||
// 剩下的都同上
|
||||
|
||||
// 向下
|
||||
while (i < n && res(i)(j) == 0) {
|
||||
res(i)(j) = num
|
||||
num += 1
|
||||
i += 1
|
||||
}
|
||||
i -= 1
|
||||
j -= 1
|
||||
|
||||
// 向左
|
||||
while (j >= 0 && res(i)(j) == 0) {
|
||||
res(i)(j) = num
|
||||
num += 1
|
||||
j -= 1
|
||||
}
|
||||
i -= 1
|
||||
j += 1
|
||||
|
||||
// 向上
|
||||
while (i >= 0 && res(i)(j) == 0) {
|
||||
res(i)(j) = num
|
||||
num += 1
|
||||
i -= 1
|
||||
}
|
||||
i += 1
|
||||
j += 1
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -347,7 +347,35 @@ var uniquePaths = function(m, n) {
|
|||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function uniquePaths(m: number, n: number): number {
|
||||
/**
|
||||
dp[i][j]: 到达(i, j)的路径数
|
||||
dp[0][*]: 1;
|
||||
dp[*][0]: 1;
|
||||
...
|
||||
dp[i][j]: dp[i - 1][j] + dp[i][j - 1];
|
||||
*/
|
||||
const dp: number[][] = new Array(m).fill(0).map(_ => []);
|
||||
for (let i = 0; i < m; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
dp[0][i] = 1;
|
||||
}
|
||||
for (let i = 1; i < m; i++) {
|
||||
for (let j = 1; j < n; j++) {
|
||||
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
|
||||
}
|
||||
}
|
||||
return dp[m - 1][n - 1];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
//初始化dp数组
|
||||
int **initDP(int m, int n) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -352,7 +354,38 @@ var uniquePathsWithObstacles = function(obstacleGrid) {
|
|||
};
|
||||
```
|
||||
|
||||
C
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function uniquePathsWithObstacles(obstacleGrid: number[][]): number {
|
||||
/**
|
||||
dp[i][j]: 到达(i, j)的路径数
|
||||
dp[0][*]: 用u表示第一个障碍物下标,则u之前为1,u之后(含u)为0
|
||||
dp[*][0]: 同上
|
||||
...
|
||||
dp[i][j]: obstacleGrid[i][j] === 1 ? 0 : dp[i-1][j] + dp[i][j-1];
|
||||
*/
|
||||
const m: number = obstacleGrid.length;
|
||||
const n: number = obstacleGrid[0].length;
|
||||
const dp: number[][] = new Array(m).fill(0).map(_ => new Array(n).fill(0));
|
||||
for (let i = 0; i < m && obstacleGrid[i][0] === 0; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
for (let i = 0; i < n && obstacleGrid[0][i] === 0; i++) {
|
||||
dp[0][i] = 1;
|
||||
}
|
||||
for (let i = 1; i < m; i++) {
|
||||
for (let j = 1; j < n; j++) {
|
||||
if (obstacleGrid[i][j] === 1) continue;
|
||||
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
|
||||
}
|
||||
}
|
||||
return dp[m - 1][n - 1];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
//初始化dp数组
|
||||
int **initDP(int m, int n, int** obstacleGrid) {
|
||||
|
|
|
|||
|
|
@ -308,7 +308,58 @@ var climbStairs = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript
|
||||
|
||||
> 爬2阶
|
||||
|
||||
```typescript
|
||||
function climbStairs(n: number): number {
|
||||
/**
|
||||
dp[i]: i阶楼梯的方法种数
|
||||
dp[1]: 1;
|
||||
dp[2]: 2;
|
||||
...
|
||||
dp[i]: dp[i - 1] + dp[i - 2];
|
||||
*/
|
||||
const dp: number[] = [];
|
||||
dp[1] = 1;
|
||||
dp[2] = 2;
|
||||
for (let i = 3; i <= n; i++) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
> 爬m阶
|
||||
|
||||
```typescript
|
||||
function climbStairs(n: number): number {
|
||||
/**
|
||||
一次可以爬m阶
|
||||
dp[i]: i阶楼梯的方法种数
|
||||
dp[1]: 1;
|
||||
dp[2]: 2;
|
||||
dp[3]: dp[2] + dp[1];
|
||||
...
|
||||
dp[i]: dp[i - 1] + dp[i - 2] + ... + dp[max(i - m, 1)]; 从i-1加到max(i-m, 1)
|
||||
*/
|
||||
const m: number = 2; // 本题m为2
|
||||
const dp: number[] = new Array(n + 1).fill(0);
|
||||
dp[1] = 1;
|
||||
dp[2] = 2;
|
||||
for (let i = 3; i <= n; i++) {
|
||||
const end: number = Math.max(i - m, 1);
|
||||
for (let j = i - 1; j >= end; j--) {
|
||||
dp[i] += dp[j];
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
int climbStairs(int n){
|
||||
//若n<=2,返回n
|
||||
|
|
|
|||
|
|
@ -199,6 +199,28 @@ var climbStairs = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function climbStairs(n: number): number {
|
||||
const m: number = 2; // 本题m为2
|
||||
const dp: number[] = new Array(n + 1).fill(0);
|
||||
dp[0] = 1;
|
||||
// 遍历背包
|
||||
for (let i = 1; i <= n; i++) {
|
||||
// 遍历物品
|
||||
for (let j = 1; j <= m; j++) {
|
||||
if (j <= i) {
|
||||
dp[i] += dp[i - j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -327,5 +327,42 @@ const minDistance = (word1, word2) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,最少操作数
|
||||
dp[0][0]=0:表示word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 0; i <= length1; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let i = 0; i <= length2; i++) {
|
||||
dp[0][i] = i;
|
||||
}
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(
|
||||
dp[i - 1][j],
|
||||
dp[i][j - 1],
|
||||
dp[i - 1][j - 1]
|
||||
) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
也可以直接看我的B站视频:[带你学透回溯算法-组合问题(对应力扣题目:77.组合)](https://www.bilibili.com/video/BV1ti4y1L7cv#reply3733925949)
|
||||
|
||||
# 思路
|
||||
## 思路
|
||||
|
||||
|
||||
本题这是回溯法的经典题目。
|
||||
|
|
@ -232,7 +232,7 @@ void backtracking(参数) {
|
|||
|
||||
**对比一下本题的代码,是不是发现有点像!** 所以有了这个模板,就有解题的大体方向,不至于毫无头绪。
|
||||
|
||||
# 总结
|
||||
## 总结
|
||||
|
||||
组合问题是回溯法解决的经典问题,我们开始的时候给大家列举一个很形象的例子,就是n为100,k为50的话,直接想法就需要50层for循环。
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ void backtracking(参数) {
|
|||
|
||||
接着用回溯法三部曲,逐步分析了函数参数、终止条件和单层搜索的过程。
|
||||
|
||||
# 剪枝优化
|
||||
## 剪枝优化
|
||||
|
||||
我们说过,回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的。
|
||||
|
||||
|
|
@ -324,7 +324,7 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
# 剪枝总结
|
||||
## 剪枝总结
|
||||
|
||||
本篇我们准对求组合问题的回溯法代码做了剪枝优化,这个优化如果不画图的话,其实不好理解,也不好讲清楚。
|
||||
|
||||
|
|
@ -334,10 +334,10 @@ public:
|
|||
|
||||
|
||||
|
||||
# 其他语言版本
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
## Java:
|
||||
### Java:
|
||||
```java
|
||||
class Solution {
|
||||
List<List<Integer>> result = new ArrayList<>();
|
||||
|
|
@ -366,6 +366,8 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
Python2:
|
||||
```python
|
||||
class Solution(object):
|
||||
|
|
@ -395,7 +397,6 @@ class Solution(object):
|
|||
return result
|
||||
```
|
||||
|
||||
## Python
|
||||
```python
|
||||
class Solution:
|
||||
def combine(self, n: int, k: int) -> List[List[int]]:
|
||||
|
|
@ -432,7 +433,7 @@ class Solution:
|
|||
```
|
||||
|
||||
|
||||
## javascript
|
||||
### javascript
|
||||
|
||||
剪枝:
|
||||
```javascript
|
||||
|
|
@ -456,7 +457,7 @@ const combineHelper = (n, k, startIndex) => {
|
|||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function combine(n: number, k: number): number[][] {
|
||||
|
|
@ -479,7 +480,7 @@ function combine(n: number, k: number): number[][] {
|
|||
|
||||
|
||||
|
||||
## Go
|
||||
### Go
|
||||
```Go
|
||||
var res [][]int
|
||||
func combine(n int, k int) [][]int {
|
||||
|
|
@ -534,7 +535,7 @@ func backtrack(n,k,start int,track []int){
|
|||
}
|
||||
```
|
||||
|
||||
## C
|
||||
### C
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
|
|
@ -642,7 +643,7 @@ int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
|
|||
}
|
||||
```
|
||||
|
||||
## Swift
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
func combine(_ n: Int, _ k: Int) -> [[Int]] {
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ var subsets = function(nums) {
|
|||
let result = []
|
||||
let path = []
|
||||
function backtracking(startIndex) {
|
||||
result.push(path.slice())
|
||||
result.push([...path])
|
||||
for(let i = startIndex; i < nums.length; i++) {
|
||||
path.push(nums[i])
|
||||
backtracking(i + 1)
|
||||
|
|
@ -280,7 +280,7 @@ function subsets(nums: number[]): number[][] {
|
|||
backTracking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTracking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
let length = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
|
|
|
|||
|
|
@ -486,5 +486,95 @@ var largestRectangleArea = function(heights) {
|
|||
return maxArea;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
> 双指针法(会超时):
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
let resMax: number = 0;
|
||||
for (let i = 0, length = heights.length; i < length; i++) {
|
||||
// 左开右开
|
||||
let left: number = i - 1,
|
||||
right: number = i + 1;
|
||||
while (left >= 0 && heights[left] >= heights[i]) {
|
||||
left--;
|
||||
}
|
||||
while (right < length && heights[right] >= heights[i]) {
|
||||
right++;
|
||||
}
|
||||
resMax = Math.max(resMax, heights[i] * (right - left - 1));
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划预处理:
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
const length: number = heights.length;
|
||||
const leftHeightDp: number[] = [],
|
||||
rightHeightDp: number[] = [];
|
||||
leftHeightDp[0] = -1;
|
||||
rightHeightDp[length - 1] = length;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let j = i - 1;
|
||||
while (j >= 0 && heights[i] <= heights[j]) {
|
||||
j = leftHeightDp[j];
|
||||
}
|
||||
leftHeightDp[i] = j;
|
||||
}
|
||||
for (let i = length - 2; i >= 0; i--) {
|
||||
let j = i + 1;
|
||||
while (j < length && heights[i] <= heights[j]) {
|
||||
j = rightHeightDp[j];
|
||||
}
|
||||
rightHeightDp[i] = j;
|
||||
}
|
||||
let resMax: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let area = heights[i] * (rightHeightDp[i] - leftHeightDp[i] - 1);
|
||||
resMax = Math.max(resMax, area);
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
> 单调栈:
|
||||
|
||||
```typescript
|
||||
function largestRectangleArea(heights: number[]): number {
|
||||
heights.push(0);
|
||||
const length: number = heights.length;
|
||||
// 栈底->栈顶:严格单调递增
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
let resMax: number = 0;
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
if (heights[top] < heights[i]) {
|
||||
stack.push(i);
|
||||
} else if (heights[top] === heights[i]) {
|
||||
stack.pop();
|
||||
stack.push(i);
|
||||
} else {
|
||||
while (stack.length > 0 && heights[top] > heights[i]) {
|
||||
let mid = stack.pop();
|
||||
let left = stack.length > 0 ? stack[stack.length - 1] : -1;
|
||||
let w = i - left - 1;
|
||||
let h = heights[mid];
|
||||
resMax = Math.max(resMax, w * h);
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resMax;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ var subsetsWithDup = function(nums) {
|
|||
return a - b
|
||||
})
|
||||
function backtracing(startIndex, sortNums) {
|
||||
result.push(path.slice(0))
|
||||
result.push([...path])
|
||||
if(startIndex > nums.length - 1) {
|
||||
return
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ function subsetsWithDup(nums: number[]): number[][] {
|
|||
backTraking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTraking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
resArr.push([...route]);
|
||||
let length: number = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ var restoreIpAddresses = function(s) {
|
|||
return;
|
||||
}
|
||||
for(let j = i; j < s.length; j++) {
|
||||
const str = s.substr(i, j - i + 1);
|
||||
const str = s.slice(i, j + 1);
|
||||
if(str.length > 3 || +str > 255) break;
|
||||
if(str.length > 1 && str[0] === "0") break;
|
||||
path.push(str);
|
||||
|
|
|
|||
|
|
@ -227,7 +227,33 @@ const numTrees =(n) => {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
TypeScript
|
||||
|
||||
```typescript
|
||||
function numTrees(n: number): number {
|
||||
/**
|
||||
dp[i]: i个节点对应的种树
|
||||
dp[0]: -1; 无意义;
|
||||
dp[1]: 1;
|
||||
...
|
||||
dp[i]: 2 * dp[i - 1] +
|
||||
(dp[1] * dp[i - 2] + dp[2] * dp[i - 3] + ... + dp[i - 2] * dp[1]); 从1加到i-2
|
||||
*/
|
||||
const dp: number[] = [];
|
||||
dp[0] = -1; // 表示无意义
|
||||
dp[1] = 1;
|
||||
for (let i = 2; i <= n; i++) {
|
||||
dp[i] = 2 * dp[i - 1];
|
||||
for (let j = 1, end = i - 1; j < end; j++) {
|
||||
dp[i] += dp[j] * dp[end - j];
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
//开辟dp数组
|
||||
int *initDP(int n) {
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
# 总结
|
||||
## 总结
|
||||
|
||||
这次我们又深度剖析了一道二叉树的“简单题”,大家会发现,真正的把题目搞清楚其实并不简单,leetcode上accept了和真正掌握了还是有距离的。
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ public:
|
|||
|
||||
如果已经做过这道题目的同学,读完文章可以再去看看这道题目,思考一下,会有不一样的发现!
|
||||
|
||||
# 相关题目推荐
|
||||
## 相关题目推荐
|
||||
|
||||
这两道题目基本和本题是一样的,只要稍加修改就可以AC。
|
||||
|
||||
|
|
@ -725,5 +725,25 @@ func isSymmetric3(_ root: TreeNode?) -> Bool {
|
|||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
def isSymmetric(root: TreeNode): Boolean = {
|
||||
if (root == null) return true // 如果等于空直接返回true
|
||||
def compare(left: TreeNode, right: TreeNode): Boolean = {
|
||||
if (left == null && right == null) return true // 如果左右都为空,则为true
|
||||
if (left == null && right != null) return false // 如果左空右不空,不对称,返回false
|
||||
if (left != null && right == null) return false // 如果左不空右空,不对称,返回false
|
||||
// 如果左右的值相等,并且往下递归
|
||||
left.value == right.value && compare(left.left, right.right) && compare(left.right, right.left)
|
||||
}
|
||||
// 分别比较左子树和右子树
|
||||
compare(root.left, root.right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,26 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
```CPP
|
||||
# 递归法
|
||||
class Solution {
|
||||
public:
|
||||
void order(TreeNode* cur, vector<vector<int>>& result, int depth)
|
||||
{
|
||||
if (cur == nullptr) return;
|
||||
if (result.size() == depth) result.push_back(vector<int>());
|
||||
result[depth].push_back(cur->val);
|
||||
order(cur->left, result, depth + 1);
|
||||
order(cur->right, result, depth + 1);
|
||||
}
|
||||
vector<vector<int>> levelOrder(TreeNode* root) {
|
||||
vector<vector<int>> result;
|
||||
int depth = 0;
|
||||
order(root, result, depth);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
python3代码:
|
||||
|
||||
|
|
@ -298,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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!**
|
||||
|
||||
|
|
@ -528,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/)
|
||||
|
|
@ -750,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/)
|
||||
|
|
@ -981,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叉树的层序遍历
|
||||
|
||||
|
|
@ -1225,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/)
|
||||
|
|
@ -1433,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/)
|
||||
|
|
@ -1692,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/)
|
||||
|
|
@ -1943,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/)
|
||||
|
|
@ -2160,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/)
|
||||
|
|
@ -2379,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叉树的最大深度
|
||||
|
|
@ -468,7 +495,7 @@ class solution:
|
|||
|
||||
|
||||
## go
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
```go
|
||||
/**
|
||||
* definition for a binary tree node.
|
||||
|
|
@ -521,6 +548,8 @@ func maxdepth(root *treenode) int {
|
|||
|
||||
## javascript
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```javascript
|
||||
var maxdepth = function(root) {
|
||||
if (root === null) return 0;
|
||||
|
|
@ -568,6 +597,8 @@ var maxDepth = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
N叉树的最大深度 递归写法
|
||||
```js
|
||||
var maxDepth = function(root) {
|
||||
|
|
@ -600,9 +631,9 @@ var maxDepth = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript:
|
||||
## TypeScript
|
||||
|
||||
> 二叉树的最大深度:
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
|
|
@ -645,7 +676,7 @@ function maxDepth(root: TreeNode | null): number {
|
|||
};
|
||||
```
|
||||
|
||||
> N叉树的最大深度
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
|
|
@ -675,6 +706,8 @@ function maxDepth(root: TreeNode | null): number {
|
|||
|
||||
## C
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
二叉树最大深度递归
|
||||
```c
|
||||
int maxDepth(struct TreeNode* root){
|
||||
|
|
@ -731,7 +764,8 @@ int maxDepth(struct TreeNode* root){
|
|||
|
||||
## Swift
|
||||
|
||||
>二叉树最大深度
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```swift
|
||||
// 递归 - 后序
|
||||
func maxDepth1(_ root: TreeNode?) -> Int {
|
||||
|
|
@ -770,7 +804,8 @@ func maxDepth(_ root: TreeNode?) -> Int {
|
|||
}
|
||||
```
|
||||
|
||||
>N叉树最大深度
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
```swift
|
||||
// 递归
|
||||
func maxDepth(_ root: Node?) -> Int {
|
||||
|
|
@ -806,5 +841,84 @@ func maxDepth1(_ root: Node?) -> Int {
|
|||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
### 104.二叉树的最大深度
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def maxDepth(root: TreeNode): Int = {
|
||||
def process(curNode: TreeNode): Int = {
|
||||
if (curNode == null) return 0
|
||||
// 递归左节点和右节点,返回最大的,最后+1
|
||||
math.max(process(curNode.left), process(curNode.right)) + 1
|
||||
}
|
||||
// 调用递归方法,return关键字可以省略
|
||||
process(root)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def maxDepth(root: TreeNode): Int = {
|
||||
var depth = 0
|
||||
if (root == null) return depth
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
depth += 1 // 只要有层次就+=1
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def maxDepth(root: Node): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
for (node <- root.children) {
|
||||
depth = math.max(depth, maxDepth(node))
|
||||
}
|
||||
depth + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法: (层序遍历)
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def maxDepth(root: Node): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
val queue = mutable.Queue[Node]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
depth += 1
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
for (node <- curNode.children) queue.enqueue(node)
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ int getHeight(TreeNode* node) {
|
|||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
|
||||
int getHeight(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -488,5 +488,110 @@ func minDepth(_ root: TreeNode?) -> Int {
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
## Scala
|
||||
|
||||
递归法:
|
||||
```scala
|
||||
object Solution {
|
||||
def minDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
if (root.left == null && root.right != null) return 1 + minDepth(root.right)
|
||||
if (root.left != null && root.right == null) return 1 + minDepth(root.left)
|
||||
// 如果两侧都不为空,则取最小值,return关键字可以省略
|
||||
1 + math.min(minDepth(root.left), minDepth(root.right))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def minDepth(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
var depth = 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
depth += 1
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
if (curNode.left == null && curNode.right == null) return depth
|
||||
}
|
||||
}
|
||||
depth
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
rust:
|
||||
```rust
|
||||
impl Solution {
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -377,22 +377,22 @@ class solution {
|
|||
|
||||
```java
|
||||
class solution {
|
||||
public list<list<integer>> pathsum(treenode root, int targetsum) {
|
||||
list<list<integer>> res = new arraylist<>();
|
||||
public List<List<Integer>> pathsum(TreeNode root, int targetsum) {
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
if (root == null) return res; // 非空判断
|
||||
|
||||
list<integer> path = new linkedlist<>();
|
||||
List<Integer> path = new LinkedList<>();
|
||||
preorderdfs(root, targetsum, res, path);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void preorderdfs(treenode root, int targetsum, list<list<integer>> res, list<integer> path) {
|
||||
public void preorderdfs(TreeNode root, int targetsum, List<List<Integer>> res, List<Integer> path) {
|
||||
path.add(root.val);
|
||||
// 遇到了叶子节点
|
||||
if (root.left == null && root.right == null) {
|
||||
// 找到了和为 targetsum 的路径
|
||||
if (targetsum - root.val == 0) {
|
||||
res.add(new arraylist<>(path));
|
||||
res.add(new ArrayList<>(path));
|
||||
}
|
||||
return; // 如果和不为 targetsum,返回
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,6 +267,36 @@ const numDistinct = (s, t) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function numDistinct(s: string, t: string): number {
|
||||
/**
|
||||
dp[i][j]: s前i个字符,t前j个字符,s子序列中t出现的个数
|
||||
dp[0][0]=1, 表示s前0个字符为'',t前0个字符为''
|
||||
*/
|
||||
const sLen: number = s.length,
|
||||
tLen: number = t.length;
|
||||
const dp: number[][] = new Array(sLen + 1).fill(0)
|
||||
.map(_ => new Array(tLen + 1).fill(0));
|
||||
for (let m = 0; m < sLen; m++) {
|
||||
dp[m][0] = 1;
|
||||
}
|
||||
for (let i = 1; i <= sLen; i++) {
|
||||
for (let j = 1; j <= tLen; j++) {
|
||||
if (s[i - 1] === t[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[sLen][tLen];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
> 版本一:
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ var partition = function(s) {
|
|||
}
|
||||
for(let j = i; j < len; j++) {
|
||||
if(!isPalindrome(s, i, j)) continue;
|
||||
path.push(s.substr(i, j - i + 1));
|
||||
path.push(s.slice(i, j + 1));
|
||||
backtracking(j + 1);
|
||||
path.pop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,6 +213,25 @@ func findMax(num1 int ,num2 int) int{
|
|||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
pub fn candy(ratings: Vec<i32>) -> i32 {
|
||||
let mut candies = vec![1i32; ratings.len()];
|
||||
for i in 1..ratings.len() {
|
||||
if ratings[i - 1] < ratings[i] {
|
||||
candies[i] = candies[i - 1] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
for i in (0..ratings.len()-1).rev() {
|
||||
if ratings[i] > ratings[i + 1] {
|
||||
candies[i] = candies[i].max(candies[i + 1] + 1);
|
||||
}
|
||||
}
|
||||
candies.iter().sum()
|
||||
}
|
||||
```
|
||||
|
||||
### Javascript:
|
||||
```Javascript
|
||||
var candy = function(ratings) {
|
||||
|
|
|
|||
|
|
@ -345,6 +345,48 @@ const wordBreak = (s, wordDict) => {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划
|
||||
|
||||
```typescript
|
||||
function wordBreak(s: string, wordDict: string[]): boolean {
|
||||
const dp: boolean[] = new Array(s.length + 1).fill(false);
|
||||
dp[0] = true;
|
||||
for (let i = 1; i <= s.length; i++) {
|
||||
for (let j = 0; j < i; j++) {
|
||||
const tempStr: string = s.slice(j, i);
|
||||
if (wordDict.includes(tempStr) && dp[j] === true) {
|
||||
dp[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[s.length];
|
||||
};
|
||||
```
|
||||
|
||||
> 记忆化回溯
|
||||
|
||||
```typescript
|
||||
function wordBreak(s: string, wordDict: string[]): boolean {
|
||||
// 只需要记忆结果为false的情况
|
||||
const memory: boolean[] = [];
|
||||
return backTracking(s, wordDict, 0, memory);
|
||||
function backTracking(s: string, wordDict: string[], startIndex: number, memory: boolean[]): boolean {
|
||||
if (startIndex >= s.length) return true;
|
||||
if (memory[startIndex] === false) return false;
|
||||
for (let i = startIndex + 1, length = s.length; i <= length; i++) {
|
||||
const str: string = s.slice(startIndex, i);
|
||||
if (wordDict.includes(str) && backTracking(s, wordDict, i, memory))
|
||||
return true;
|
||||
}
|
||||
memory[startIndex] = false;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -189,6 +189,29 @@ const rob = nums => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function rob(nums: number[]): number {
|
||||
/**
|
||||
dp[i]: 前i个房屋能偷到的最大金额
|
||||
dp[0]: nums[0];
|
||||
dp[1]: max(nums[0], nums[1]);
|
||||
...
|
||||
dp[i]: max(dp[i-1], dp[i-2]+nums[i]);
|
||||
*/
|
||||
const length: number = nums.length;
|
||||
if (length === 1) return nums[0];
|
||||
const dp: number[] = [];
|
||||
dp[0] = nums[0];
|
||||
dp[1] = Math.max(nums[0], nums[1]);
|
||||
for (let i = 2; i < length; i++) {
|
||||
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
|
||||
}
|
||||
return dp[length - 1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -385,5 +385,62 @@ 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:
|
||||
|
|
@ -478,6 +499,36 @@ impl Solution {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
```scala
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* class ListNode(_x: Int = 0, _next: ListNode = null) {
|
||||
* var next: ListNode = _next
|
||||
* var x: Int = _x
|
||||
* }
|
||||
*/
|
||||
object Solution {
|
||||
def removeElements(head: ListNode, `val`: Int): ListNode = {
|
||||
if (head == null) return head
|
||||
var dummy = new ListNode(-1, head) // 定义虚拟头节点
|
||||
var cur = head // cur 表示当前节点
|
||||
var pre = dummy // pre 表示cur前一个节点
|
||||
while (cur != null) {
|
||||
if (cur.x == `val`) {
|
||||
// 相等,就删除那么cur的前一个节点pre执行cur的下一个
|
||||
pre.next = cur.next
|
||||
} else {
|
||||
// 不相等,pre就等于当前cur节点
|
||||
pre = cur
|
||||
}
|
||||
// 向下迭代
|
||||
cur = cur.next
|
||||
}
|
||||
// 最终返回dummy的下一个,就是链表的头
|
||||
dummy.next
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
## 209.长度最小的子数组
|
||||
# 209.长度最小的子数组
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
|
||||
|
||||
|
|
@ -17,6 +17,9 @@
|
|||
输出:2
|
||||
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
|
||||
|
||||
# 思路
|
||||
|
||||
为了易于大家理解,我特意录制了[拿下滑动窗口! | LeetCode 209 长度最小的子数组](https://www.bilibili.com/video/BV1tZ4y1q7XE)
|
||||
|
||||
## 暴力解法
|
||||
|
||||
|
|
@ -47,8 +50,8 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
时间复杂度:O(n^2)
|
||||
空间复杂度:O(1)
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
## 滑动窗口
|
||||
|
||||
|
|
@ -56,6 +59,20 @@ public:
|
|||
|
||||
所谓滑动窗口,**就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果**。
|
||||
|
||||
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
|
||||
|
||||
那么滑动窗口如何用一个for循环来完成这个操作呢。
|
||||
|
||||
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
|
||||
|
||||
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
|
||||
|
||||
此时难免再次陷入 暴力解法的怪圈。
|
||||
|
||||
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
|
||||
|
||||
那么问题来了, 滑动窗口的起始位置如何移动呢?
|
||||
|
||||
这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
|
||||
|
||||

|
||||
|
|
@ -74,7 +91,7 @@ public:
|
|||
|
||||
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
|
||||
|
||||
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
|
||||
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
|
||||
|
||||
解题的关键在于 窗口的起始位置如何移动,如图所示:
|
||||
|
||||
|
|
@ -107,8 +124,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
时间复杂度:O(n)
|
||||
空间复杂度:O(1)
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
**一些录友会疑惑为什么时间复杂度是O(n)**。
|
||||
|
||||
|
|
@ -400,6 +417,54 @@ class Solution {
|
|||
}
|
||||
}
|
||||
```
|
||||
Scala:
|
||||
|
||||
滑动窗口:
|
||||
```scala
|
||||
object Solution {
|
||||
def minSubArrayLen(target: Int, nums: Array[Int]): Int = {
|
||||
var result = Int.MaxValue // 返回结果,默认最大值
|
||||
var left = 0 // 慢指针,当sum>=target,向右移动
|
||||
var sum = 0 // 窗口值的总和
|
||||
for (right <- 0 until nums.length) {
|
||||
sum += nums(right)
|
||||
while (sum >= target) {
|
||||
result = math.min(result, right - left + 1) // 产生新结果
|
||||
sum -= nums(left) // 左指针移动,窗口总和减去左指针的值
|
||||
left += 1 // 左指针向右移动
|
||||
}
|
||||
}
|
||||
// 相当于三元运算符,return关键字可以省略
|
||||
if (result == Int.MaxValue) 0 else result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
暴力解法:
|
||||
```scala
|
||||
object Solution {
|
||||
def minSubArrayLen(target: Int, nums: Array[Int]): Int = {
|
||||
import scala.util.control.Breaks
|
||||
var res = Int.MaxValue
|
||||
var subLength = 0
|
||||
for (i <- 0 until nums.length) {
|
||||
var sum = 0
|
||||
Breaks.breakable(
|
||||
for (j <- i until nums.length) {
|
||||
sum += nums(j)
|
||||
if (sum >= target) {
|
||||
subLength = j - i + 1
|
||||
res = math.min(subLength, res)
|
||||
Breaks.break()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// 相当于三元运算符
|
||||
if (res == Int.MaxValue) 0 else res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -165,7 +165,30 @@ const robRange = (nums, start, end) => {
|
|||
return dp[end]
|
||||
}
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function rob(nums: number[]): number {
|
||||
const length: number = nums.length;
|
||||
if (length === 0) return 0;
|
||||
if (length === 1) return nums[0];
|
||||
return Math.max(robRange(nums, 0, length - 2),
|
||||
robRange(nums, 1, length - 1));
|
||||
};
|
||||
function robRange(nums: number[], start: number, end: number): number {
|
||||
if (start === end) return nums[start];
|
||||
const dp: number[] = [];
|
||||
dp[start] = nums[start];
|
||||
dp[start + 1] = Math.max(nums[start], nums[start + 1]);
|
||||
for (let i = start + 2; i <= end; i++) {
|
||||
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
|
||||
}
|
||||
return dp[end];
|
||||
}
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
// 打家劫舍Ⅱ 动态规划
|
||||
// 时间复杂度O(n) 空间复杂度O(n)
|
||||
|
|
|
|||
|
|
@ -360,39 +360,30 @@ func backTree(n,k,startIndex int,track *[]int,result *[][]int){
|
|||
## javaScript
|
||||
|
||||
```js
|
||||
// 等差数列
|
||||
var maxV = k => k * (9 + 10 - k) / 2;
|
||||
var minV = k => k * (1 + k) / 2;
|
||||
/**
|
||||
* @param {number} k
|
||||
* @param {number} n
|
||||
* @return {number[][]}
|
||||
*/
|
||||
var combinationSum3 = function(k, n) {
|
||||
if (k > 9 || k < 1) return [];
|
||||
// if (n > maxV(k) || n < minV(k)) return [];
|
||||
// if (n === maxV(k)) return [Array.from({length: k}).map((v, i) => 9 - i)];
|
||||
// if (n === minV(k)) return [Array.from({length: k}).map((v, i) => i + 1)];
|
||||
|
||||
const res = [], path = [];
|
||||
backtracking(k, n, 1, 0);
|
||||
return res;
|
||||
function backtracking(k, n, i, sum){
|
||||
const len = path.length;
|
||||
if (len > k || sum > n) return;
|
||||
if (maxV(k - len) < n - sum) return;
|
||||
if (minV(k - len) > n - sum) return;
|
||||
|
||||
if(len === k && sum == n) {
|
||||
res.push(Array.from(path));
|
||||
const backtrack = (start) => {
|
||||
const l = path.length;
|
||||
if (l === k) {
|
||||
const sum = path.reduce((a, b) => a + b);
|
||||
if (sum === n) {
|
||||
res.push([...path]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const min = Math.min(n - sum, 9 + len - k + 1);
|
||||
|
||||
for(let a = i; a <= min; a++) {
|
||||
path.push(a);
|
||||
sum += a;
|
||||
backtracking(k, n, a + 1, sum);
|
||||
for (let i = start; i <= 9 - (k - l) + 1; i++) {
|
||||
path.push(i);
|
||||
backtrack(i + 1);
|
||||
path.pop();
|
||||
sum -= a;
|
||||
}
|
||||
}
|
||||
let res = [], path = [];
|
||||
backtrack(1);
|
||||
return res;
|
||||
};
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -646,5 +646,68 @@ func countNodes(_ root: TreeNode?) -> Int {
|
|||
}
|
||||
```
|
||||
|
||||
## Scala
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if(root == null) return 0
|
||||
1 + countNodes(root.left) + countNodes(root.right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
层序遍历:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
var node = 0
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
node += 1
|
||||
val curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
}
|
||||
}
|
||||
node
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
利用完全二叉树性质:
|
||||
```scala
|
||||
object Solution {
|
||||
def countNodes(root: TreeNode): Int = {
|
||||
if (root == null) return 0
|
||||
var leftNode = root.left
|
||||
var rightNode = root.right
|
||||
// 向左向右往下探
|
||||
var leftDepth = 0
|
||||
while (leftNode != null) {
|
||||
leftDepth += 1
|
||||
leftNode = leftNode.left
|
||||
}
|
||||
var rightDepth = 0
|
||||
while (rightNode != null) {
|
||||
rightDepth += 1
|
||||
rightNode = rightNode.right
|
||||
}
|
||||
// 如果相等就是一个满二叉树
|
||||
if (leftDepth == rightDepth) {
|
||||
return (2 << leftDepth) - 1
|
||||
}
|
||||
// 如果不相等就不是一个完全二叉树,继续向下递归
|
||||
countNodes(root.left) + countNodes(root.right) + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -820,5 +818,53 @@ func invertTree(_ root: TreeNode?) -> TreeNode? {
|
|||
}
|
||||
```
|
||||
|
||||
### Scala
|
||||
|
||||
深度优先遍历(前序遍历):
|
||||
```scala
|
||||
object Solution {
|
||||
def invertTree(root: TreeNode): TreeNode = {
|
||||
if (root == null) return root
|
||||
// 递归
|
||||
def process(node: TreeNode): Unit = {
|
||||
if (node == null) return
|
||||
// 翻转节点
|
||||
val curNode = node.left
|
||||
node.left = node.right
|
||||
node.right = curNode
|
||||
process(node.left)
|
||||
process(node.right)
|
||||
}
|
||||
process(root)
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
广度优先遍历(层序遍历):
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable
|
||||
def invertTree(root: TreeNode): TreeNode = {
|
||||
if (root == null) return root
|
||||
val queue = mutable.Queue[TreeNode]()
|
||||
queue.enqueue(root)
|
||||
while (!queue.isEmpty) {
|
||||
val len = queue.size
|
||||
for (i <- 0 until len) {
|
||||
var curNode = queue.dequeue()
|
||||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||||
// 翻转
|
||||
var tmpNode = curNode.left
|
||||
curNode.left = curNode.right
|
||||
curNode.right = tmpNode
|
||||
}
|
||||
}
|
||||
root
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -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.赎金信
|
||||
|
|
|
|||
|
|
@ -702,5 +702,35 @@ func binaryTreePaths(_ root: TreeNode?) -> [String] {
|
|||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
递归:
|
||||
```scala
|
||||
object Solution {
|
||||
import scala.collection.mutable.ListBuffer
|
||||
def binaryTreePaths(root: TreeNode): List[String] = {
|
||||
val res = ListBuffer[String]()
|
||||
def traversal(curNode: TreeNode, path: ListBuffer[Int]): Unit = {
|
||||
path.append(curNode.value)
|
||||
if (curNode.left == null && curNode.right == null) {
|
||||
res.append(path.mkString("->")) // mkString函数: 将数组的所有值按照指定字符串拼接
|
||||
return // 处理完可以直接return
|
||||
}
|
||||
|
||||
if (curNode.left != null) {
|
||||
traversal(curNode.left, path)
|
||||
path.remove(path.size - 1)
|
||||
}
|
||||
if (curNode.right != null) {
|
||||
traversal(curNode.right, path)
|
||||
path.remove(path.size - 1)
|
||||
}
|
||||
}
|
||||
traversal(root, ListBuffer[Int]())
|
||||
res.toList
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -355,5 +355,24 @@ var numSquares2 = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function numSquares(n: number): number {
|
||||
const goodsNum: number = Math.floor(Math.sqrt(n));
|
||||
const dp: number[] = new Array(n + 1).fill(Infinity);
|
||||
dp[0] = 0;
|
||||
for (let i = 1; i <= goodsNum; i++) {
|
||||
const tempVal: number = i * i;
|
||||
for (let j = tempVal; j <= n; j++) {
|
||||
dp[j] = Math.min(dp[j], dp[j - tempVal] + 1);
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -201,6 +201,23 @@ func max(x, y int) int {
|
|||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
pub fn length_of_lis(nums: Vec<i32>) -> i32 {
|
||||
let mut dp = vec![1; nums.len() + 1];
|
||||
let mut result = 1;
|
||||
for i in 1..nums.len() {
|
||||
for j in 0..i {
|
||||
if nums[j] < nums[i] {
|
||||
dp[i] = dp[i].max(dp[j] + 1);
|
||||
}
|
||||
result = result.max(dp[i]);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
```
|
||||
|
||||
Javascript
|
||||
```javascript
|
||||
const lengthOfLIS = (nums) => {
|
||||
|
|
@ -220,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>
|
||||
|
|
|
|||
|
|
@ -302,6 +302,24 @@ func min(a, b int) int {
|
|||
|
||||
```
|
||||
|
||||
Rust:
|
||||
|
||||
```rust
|
||||
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {
|
||||
let amount = amount as usize;
|
||||
let mut dp = vec![i32::MAX; amount + 1];
|
||||
dp[0] = 0;
|
||||
for i in 0..coins.len() {
|
||||
for j in coins[i] as usize..=amount {
|
||||
if dp[j - coins[i] as usize] != i32::MAX {
|
||||
dp[j] = dp[j].min(dp[j - coins[i] as usize] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if dp[amount] == i32::MAX { -1 } else { dp[amount] }
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
const coinChange = (coins, amount) => {
|
||||
|
|
@ -322,7 +340,21 @@ const coinChange = (coins, amount) => {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function coinChange(coins: number[], amount: number): number {
|
||||
const dp: number[] = new Array(amount + 1).fill(Infinity);
|
||||
dp[0] = 0;
|
||||
for (let i = 0; i < coins.length; i++) {
|
||||
for (let j = coins[i]; j <= amount; j++) {
|
||||
if (dp[j - coins[i]] === Infinity) continue;
|
||||
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
|
||||
}
|
||||
}
|
||||
return dp[amount] === Infinity ? -1 : dp[amount];
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<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)
|
||||
|
|
|
|||
|
|
@ -274,7 +274,33 @@ var integerBreak = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function integerBreak(n: number): number {
|
||||
/**
|
||||
dp[i]: i对应的最大乘积
|
||||
dp[2]: 1;
|
||||
...
|
||||
dp[i]: max(
|
||||
1 * dp[i - 1], 1 * (i - 1),
|
||||
2 * dp[i - 2], 2 * (i - 2),
|
||||
..., (i - 2) * dp[2], (i - 2) * 2
|
||||
);
|
||||
*/
|
||||
const dp: number[] = new Array(n + 1).fill(0);
|
||||
dp[2] = 1;
|
||||
for (let i = 3; i <= n; i++) {
|
||||
for (let j = 1; j <= i - 2; j++) {
|
||||
dp[i] = Math.max(dp[i], j * dp[i - j], j * (i - j));
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
//初始化DP数组
|
||||
int *initDP(int num) {
|
||||
|
|
|
|||
|
|
@ -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,71 @@ 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
|
||||
|
|
|
|||
|
|
@ -221,7 +221,27 @@ const combinationSum4 = (nums, target) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function combinationSum4(nums: number[], target: number): number {
|
||||
const dp: number[] = new Array(target + 1).fill(0);
|
||||
dp[0] = 1;
|
||||
// 遍历背包
|
||||
for (let i = 1; i <= target; i++) {
|
||||
// 遍历物品
|
||||
for (let j = 0, length = nums.length; j < length; j++) {
|
||||
if (i >= nums[j]) {
|
||||
dp[i] += dp[i - nums[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[target];
|
||||
};
|
||||
```
|
||||
|
||||
Rust
|
||||
|
||||
```Rust
|
||||
impl Solution {
|
||||
pub fn combination_sum4(nums: Vec<i32>, target: i32) -> i32 {
|
||||
|
|
|
|||
|
|
@ -114,23 +114,25 @@ Java:
|
|||
```Java
|
||||
class Solution {
|
||||
public boolean canConstruct(String ransomNote, String magazine) {
|
||||
//记录杂志字符串出现的次数
|
||||
int[] arr = new int[26];
|
||||
int temp;
|
||||
for (int i = 0; i < magazine.length(); i++) {
|
||||
temp = magazine.charAt(i) - 'a';
|
||||
arr[temp]++;
|
||||
// 定义一个哈希映射数组
|
||||
int[] record = new int[26];
|
||||
|
||||
// 遍历
|
||||
for(char c : magazine.toCharArray()){
|
||||
record[c - 'a'] += 1;
|
||||
}
|
||||
for (int i = 0; i < ransomNote.length(); i++) {
|
||||
temp = ransomNote.charAt(i) - 'a';
|
||||
//对于金信中的每一个字符都在数组中查找
|
||||
//找到相应位减一,否则找不到返回false
|
||||
if (arr[temp] > 0) {
|
||||
arr[temp]--;
|
||||
} else {
|
||||
|
||||
for(char c : ransomNote.toCharArray()){
|
||||
record[c - 'a'] -= 1;
|
||||
}
|
||||
|
||||
// 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
|
||||
for(int i : record){
|
||||
if(i < 0){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -361,5 +363,88 @@ 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>
|
||||
|
|
|
|||
|
|
@ -201,7 +201,32 @@ const isSubsequence = (s, t) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function isSubsequence(s: string, t: string): boolean {
|
||||
/**
|
||||
dp[i][j]: s的前i-1个,t的前j-1个,最长公共子序列的长度
|
||||
*/
|
||||
const sLen: number = s.length,
|
||||
tLen: number = t.length;
|
||||
const dp: number[][] = new Array(sLen + 1).fill(0)
|
||||
.map(_ => new Array(tLen + 1).fill(0));
|
||||
for (let i = 1; i <= sLen; i++) {
|
||||
for (let j = 1; j <= tLen; j++) {
|
||||
if (s[i - 1] === t[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[sLen][tLen] === s.length;
|
||||
};
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func isSubsequence(s string, t string) bool {
|
||||
dp := make([][]int,len(s)+1)
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -416,6 +416,27 @@ var canPartition = function(nums) {
|
|||
};
|
||||
```
|
||||
|
||||
|
||||
TypeScript:
|
||||
|
||||
```ts
|
||||
function canPartition(nums: number[]): boolean {
|
||||
const sum: number = nums.reduce((a: number, b: number): number => a + b);
|
||||
if (sum % 2 === 1) return false;
|
||||
const target: number = sum / 2;
|
||||
// dp[j]表示容量(总数和)为j的背包所能装下的数(下标[0, i]之间任意取)的总和(<= 容量)的最大值
|
||||
const dp: number[] = new Array(target + 1).fill(0);
|
||||
const n: number = nums.length;
|
||||
for (let i: number = 0; i < n; i++) {
|
||||
for (let j: number = target; j >= nums[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
|
||||
}
|
||||
}
|
||||
return dp[target] === target;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
C:
|
||||
二维dp:
|
||||
```c
|
||||
|
|
@ -517,6 +538,61 @@ bool canPartition(int* nums, int numsSize){
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 一维数组,简洁
|
||||
|
||||
```typescript
|
||||
function canPartition(nums: number[]): boolean {
|
||||
const sum: number = nums.reduce((pre, cur) => pre + cur);
|
||||
if (sum % 2 === 1) return false;
|
||||
const bagSize: number = sum / 2;
|
||||
const goodsNum: number = nums.length;
|
||||
const dp: number[] = new Array(bagSize + 1).fill(0);
|
||||
for (let i = 0; i < goodsNum; i++) {
|
||||
for (let j = bagSize; j >= nums[i]; j--) {
|
||||
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
|
||||
}
|
||||
}
|
||||
return dp[bagSize] === bagSize;
|
||||
};
|
||||
```
|
||||
|
||||
> 二维数组,易懂
|
||||
|
||||
```typescript
|
||||
function canPartition(nums: number[]): boolean {
|
||||
/**
|
||||
weightArr = nums;
|
||||
valueArr = nums;
|
||||
bagSize = sum / 2; (sum为nums各元素总和);
|
||||
按照0-1背包处理
|
||||
*/
|
||||
const sum: number = nums.reduce((pre, cur) => pre + cur);
|
||||
if (sum % 2 === 1) return false;
|
||||
const bagSize: number = sum / 2;
|
||||
const weightArr: number[] = nums;
|
||||
const valueArr: number[] = nums;
|
||||
const goodsNum: number = weightArr.length;
|
||||
const dp: number[][] = new Array(goodsNum)
|
||||
.fill(0)
|
||||
.map(_ => new Array(bagSize + 1).fill(0));
|
||||
for (let i = weightArr[0]; i <= bagSize; i++) {
|
||||
dp[0][i] = valueArr[0];
|
||||
}
|
||||
for (let i = 1; i < goodsNum; i++) {
|
||||
for (let j = 0; j <= bagSize; j++) {
|
||||
if (j < weightArr[i]) {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weightArr[i]] + valueArr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[goodsNum - 1][bagSize] === bagSize;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public:
|
|||
};
|
||||
```
|
||||
* 时间复杂度:O(nlog n) ,有一个快排
|
||||
* 空间复杂度:O(1)
|
||||
* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
大家此时会发现如此复杂的一个问题,代码实现却这么简单!
|
||||
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n\log n)$,因为有一个快排
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(nlog n),因为有一个快排
|
||||
* 空间复杂度:O(1),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
可以看出代码并不复杂。
|
||||
|
||||
|
|
|
|||
|
|
@ -318,5 +318,70 @@ 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>
|
||||
|
|
|
|||
|
|
@ -191,6 +191,26 @@ func findContentChildren(g []int, s []int) int {
|
|||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
pub fn find_content_children(children: Vec<i32>, cookie: Vec<i32>) -> i32 {
|
||||
let mut children = children;
|
||||
let mut cookies = cookie;
|
||||
children.sort();
|
||||
cookies.sort();
|
||||
|
||||
let (mut child, mut cookie) = (0usize, 0usize);
|
||||
while child < children.len() && cookie < cookies.len() {
|
||||
// 优先选择最小饼干喂饱孩子
|
||||
if children[child] <= cookies[cookie] {
|
||||
child += 1;
|
||||
}
|
||||
cookie += 1
|
||||
}
|
||||
child as i32
|
||||
}
|
||||
```
|
||||
|
||||
### Javascript
|
||||
```js
|
||||
var findContentChildren = function(g, s) {
|
||||
|
|
|
|||
|
|
@ -323,6 +323,129 @@ const findMaxForm = (strs, m, n) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 滚动数组,二维数组法
|
||||
|
||||
```typescript
|
||||
type BinaryInfo = { numOfZero: number, numOfOne: number };
|
||||
function findMaxForm(strs: string[], m: number, n: number): number {
|
||||
const goodsNum: number = strs.length;
|
||||
const dp: number[][] = new Array(m + 1).fill(0)
|
||||
.map(_ => new Array(n + 1).fill(0));
|
||||
for (let i = 0; i < goodsNum; i++) {
|
||||
const { numOfZero, numOfOne } = countBinary(strs[i]);
|
||||
for (let j = m; j >= numOfZero; j--) {
|
||||
for (let k = n; k >= numOfOne; k--) {
|
||||
dp[j][k] = Math.max(dp[j][k], dp[j - numOfZero][k - numOfOne] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[m][n];
|
||||
};
|
||||
function countBinary(str: string): BinaryInfo {
|
||||
let numOfZero: number = 0,
|
||||
numOfOne: number = 0;
|
||||
for (let s of str) {
|
||||
if (s === '0') {
|
||||
numOfZero++;
|
||||
} else {
|
||||
numOfOne++;
|
||||
}
|
||||
}
|
||||
return { numOfZero, numOfOne };
|
||||
}
|
||||
```
|
||||
|
||||
> 传统背包,三维数组法
|
||||
|
||||
```typescript
|
||||
type BinaryInfo = { numOfZero: number, numOfOne: number };
|
||||
function findMaxForm(strs: string[], m: number, n: number): number {
|
||||
/**
|
||||
dp[i][j][k]: 前i个物品中, 背包的0容量为j, 1容量为k, 最多能放的物品数量
|
||||
*/
|
||||
const goodsNum: number = strs.length;
|
||||
const dp: number[][][] = new Array(goodsNum).fill(0)
|
||||
.map(_ => new Array(m + 1)
|
||||
.fill(0)
|
||||
.map(_ => new Array(n + 1).fill(0))
|
||||
);
|
||||
const { numOfZero, numOfOne } = countBinary(strs[0]);
|
||||
for (let i = numOfZero; i <= m; i++) {
|
||||
for (let j = numOfOne; j <= n; j++) {
|
||||
dp[0][i][j] = 1;
|
||||
}
|
||||
}
|
||||
for (let i = 1; i < goodsNum; i++) {
|
||||
const { numOfZero, numOfOne } = countBinary(strs[i]);
|
||||
for (let j = 0; j <= m; j++) {
|
||||
for (let k = 0; k <= n; k++) {
|
||||
if (j < numOfZero || k < numOfOne) {
|
||||
dp[i][j][k] = dp[i - 1][j][k];
|
||||
} else {
|
||||
dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - numOfZero][k - numOfOne] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[dp.length - 1][m][n];
|
||||
};
|
||||
function countBinary(str: string): BinaryInfo {
|
||||
let numOfZero: number = 0,
|
||||
numOfOne: number = 0;
|
||||
for (let s of str) {
|
||||
if (s === '0') {
|
||||
numOfZero++;
|
||||
} else {
|
||||
numOfOne++;
|
||||
}
|
||||
}
|
||||
return { numOfZero, numOfOne };
|
||||
}
|
||||
```
|
||||
|
||||
> 回溯法(会超时)
|
||||
|
||||
```typescript
|
||||
function findMaxForm(strs: string[], m: number, n: number): number {
|
||||
/**
|
||||
思路:暴力枚举strs的所有子集,记录符合条件子集的最大长度
|
||||
*/
|
||||
let resMax: number = 0;
|
||||
backTrack(strs, m, n, 0, []);
|
||||
return resMax;
|
||||
function backTrack(
|
||||
strs: string[], m: number, n: number,
|
||||
startIndex: number, route: string[]
|
||||
): void {
|
||||
if (startIndex === strs.length) return;
|
||||
for (let i = startIndex, length = strs.length; i < length; i++) {
|
||||
route.push(strs[i]);
|
||||
if (isValidSubSet(route, m, n)) {
|
||||
resMax = Math.max(resMax, route.length);
|
||||
backTrack(strs, m, n, i + 1, route);
|
||||
}
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
function isValidSubSet(strs: string[], m: number, n: number): boolean {
|
||||
let zeroNum: number = 0,
|
||||
oneNum: number = 0;
|
||||
strs.forEach(str => {
|
||||
for (let s of str) {
|
||||
if (s === '0') {
|
||||
zeroNum++;
|
||||
} else {
|
||||
oneNum++;
|
||||
}
|
||||
}
|
||||
});
|
||||
return zeroNum <= m && oneNum <= n;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -352,6 +352,29 @@ const findTargetSumWays = (nums, target) => {
|
|||
```
|
||||
|
||||
|
||||
TypeScript:
|
||||
|
||||
```ts
|
||||
function findTargetSumWays(nums: number[], target: number): number {
|
||||
// 把数组分成两个组合left, right.left + right = sum, left - right = target.
|
||||
const sum: number = nums.reduce((a: number, b: number): number => a + b);
|
||||
if ((sum + target) % 2 || Math.abs(target) > sum) return 0;
|
||||
const left: number = (sum + target) / 2;
|
||||
|
||||
// 将问题转化为装满容量为left的背包有多少种方法
|
||||
// dp[i]表示装满容量为i的背包有多少种方法
|
||||
const dp: number[] = new Array(left + 1).fill(0);
|
||||
dp[0] = 1; // 装满容量为0的背包有1种方法(什么也不装)
|
||||
for (let i: number = 0; i < nums.length; i++) {
|
||||
for (let j: number = left; j >= nums[i]; j--) {
|
||||
dp[j] += dp[j - nums[i]];
|
||||
}
|
||||
}
|
||||
return dp[left];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -244,6 +244,39 @@ class Solution:
|
|||
```
|
||||
|
||||
Go:
|
||||
|
||||
> 未精简版本
|
||||
```go
|
||||
func nextGreaterElement(nums1 []int, nums2 []int) []int {
|
||||
res := make([]int, len(nums1))
|
||||
for i := range res { res[i] = -1 }
|
||||
m := make(map[int]int, len(nums1))
|
||||
for k, v := range nums1 { m[v] = k }
|
||||
|
||||
stack := []int{0}
|
||||
for i := 1; i < len(nums2); i++ {
|
||||
top := stack[len(stack)-1]
|
||||
if nums2[i] < nums2[top] {
|
||||
stack = append(stack, i)
|
||||
} else if nums2[i] == nums2[top] {
|
||||
stack = append(stack, i)
|
||||
} else {
|
||||
for len(stack) != 0 && nums2[i] > nums2[top] {
|
||||
if v, ok := m[nums2[top]]; ok {
|
||||
res[v] = nums2[i]
|
||||
}
|
||||
stack = stack[:len(stack)-1]
|
||||
if len(stack) != 0 {
|
||||
top = stack[len(stack)-1]
|
||||
}
|
||||
}
|
||||
stack = append(stack, i)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
> 精简版本
|
||||
```go
|
||||
func nextGreaterElement(nums1 []int, nums2 []int) []int {
|
||||
res := make([]int, len(nums1))
|
||||
|
|
@ -299,5 +332,36 @@ var nextGreaterElement = function (nums1, nums2) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function nextGreaterElement(nums1: number[], nums2: number[]): number[] {
|
||||
const resArr: number[] = new Array(nums1.length).fill(-1);
|
||||
const stack: number[] = [];
|
||||
const helperMap: Map<number, number> = new Map();
|
||||
nums1.forEach((num, index) => {
|
||||
helperMap.set(num, index);
|
||||
})
|
||||
stack.push(0);
|
||||
for (let i = 1, length = nums2.length; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
while (stack.length > 0 && nums2[top] < nums2[i]) {
|
||||
let index = helperMap.get(nums2[top]);
|
||||
if (index !== undefined) {
|
||||
resArr[index] = nums2[i];
|
||||
}
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
if (helperMap.get(nums2[i]) !== undefined) {
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -182,5 +182,31 @@ var nextGreaterElements = function (nums) {
|
|||
return res;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function nextGreaterElements(nums: number[]): number[] {
|
||||
const length: number = nums.length;
|
||||
const stack: number[] = [];
|
||||
stack.push(0);
|
||||
const resArr: number[] = new Array(length).fill(-1);
|
||||
for (let i = 1; i < length * 2; i++) {
|
||||
const index = i % length;
|
||||
let top = stack[stack.length - 1];
|
||||
while (stack.length > 0 && nums[top] < nums[index]) {
|
||||
resArr[top] = nums[index];
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
if (i < length) {
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -245,7 +245,29 @@ var fib = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript
|
||||
|
||||
```typescript
|
||||
function fib(n: number): number {
|
||||
/**
|
||||
dp[i]: 第i个斐波那契数
|
||||
dp[0]: 0;
|
||||
dp[1]:1;
|
||||
...
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
*/
|
||||
const dp: number[] = [];
|
||||
dp[0] = 0;
|
||||
dp[1] = 1;
|
||||
for (let i = 2; i <= n; i++) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
return dp[n];
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
动态规划:
|
||||
```c
|
||||
int fib(int n){
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -236,6 +235,35 @@ const longestPalindromeSubseq = (s) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function longestPalindromeSubseq(s: string): number {
|
||||
/**
|
||||
dp[i][j]:[i,j]区间内,最长回文子序列的长度
|
||||
*/
|
||||
const length: number = s.length;
|
||||
const dp: number[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(length).fill(0));
|
||||
for (let i = 0; i < length; i++) {
|
||||
dp[i][i] = 1;
|
||||
}
|
||||
// 自下而上,自左往右遍历
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
for (let j = i + 1; j < length; j++) {
|
||||
if (s[i] === s[j]) {
|
||||
dp[i][j] = dp[i + 1][j - 1] + 2;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[0][length - 1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -242,6 +242,22 @@ func change(amount int, coins []int) int {
|
|||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
pub fn change(amount: i32, coins: Vec<i32>) -> i32 {
|
||||
let amount = amount as usize;
|
||||
let coins = coins.iter().map(|&c|c as usize).collect::<Vec<usize>>();
|
||||
let mut dp = vec![0usize; amount + 1];
|
||||
dp[0] = 1;
|
||||
for i in 0..coins.len() {
|
||||
for j in coins[i]..=amount {
|
||||
dp[j] += dp[j - coins[i]];
|
||||
}
|
||||
}
|
||||
dp[amount] as i32
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```javascript
|
||||
const change = (amount, coins) => {
|
||||
|
|
@ -258,6 +274,21 @@ const change = (amount, coins) => {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function change(amount: number, coins: number[]): number {
|
||||
const dp: number[] = new Array(amount + 1).fill(0);
|
||||
dp[0] = 1;
|
||||
for (let i = 0, length = coins.length; i < length; i++) {
|
||||
for (let j = coins[i]; j <= amount; j++) {
|
||||
dp[j] += dp[j - coins[i]];
|
||||
}
|
||||
}
|
||||
return dp[amount];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -229,6 +229,67 @@ const minDistance = (word1, word2) => {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> dp版本一:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,所需最小步数
|
||||
dp[0][0]=0: word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 0; i <= length1; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let i = 0; i <= length2; i++) {
|
||||
dp[0][i] = i;
|
||||
}
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[length1][length2];
|
||||
};
|
||||
```
|
||||
|
||||
> dp版本二:
|
||||
|
||||
```typescript
|
||||
function minDistance(word1: string, word2: string): number {
|
||||
/**
|
||||
dp[i][j]: word1前i个字符,word2前j个字符,最长公共子序列的长度
|
||||
dp[0][0]=0: word1前0个字符为'', word2前0个字符为''
|
||||
*/
|
||||
const length1: number = word1.length,
|
||||
length2: number = word2.length;
|
||||
const dp: number[][] = new Array(length1 + 1).fill(0)
|
||||
.map(_ => new Array(length2 + 1).fill(0));
|
||||
for (let i = 1; i <= length1; i++) {
|
||||
for (let j = 1; j <= length2; j++) {
|
||||
if (word1[i - 1] === word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||||
} else {
|
||||
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const maxLen: number = dp[length1][length2];
|
||||
return length1 + length2 - maxLen * 2;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -406,6 +406,63 @@ const countSubstrings = (s) => {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 动态规划:
|
||||
|
||||
```typescript
|
||||
function countSubstrings(s: string): number {
|
||||
/**
|
||||
dp[i][j]: [i,j]区间内的字符串是否为回文(左闭右闭)
|
||||
*/
|
||||
const length: number = s.length;
|
||||
const dp: boolean[][] = new Array(length).fill(0)
|
||||
.map(_ => new Array(length).fill(false));
|
||||
let resCount: number = 0;
|
||||
// 自下而上,自左向右遍历
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
for (let j = i; j < length; j++) {
|
||||
if (
|
||||
s[i] === s[j] &&
|
||||
(j - i <= 1 || dp[i + 1][j - 1] === true)
|
||||
) {
|
||||
dp[i][j] = true;
|
||||
resCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
```
|
||||
|
||||
> 双指针法:
|
||||
|
||||
```typescript
|
||||
function countSubstrings(s: string): number {
|
||||
const length: number = s.length;
|
||||
let resCount: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
resCount += expandRange(s, i, i);
|
||||
resCount += expandRange(s, i, i + 1);
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
function expandRange(s: string, left: number, right: number): number {
|
||||
let palindromeNum: number = 0;
|
||||
while (
|
||||
left >= 0 && right < s.length &&
|
||||
s[left] === s[right]
|
||||
) {
|
||||
palindromeNum++;
|
||||
left--;
|
||||
right++;
|
||||
}
|
||||
return palindromeNum;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ class Solution:
|
|||
return result
|
||||
```
|
||||
|
||||
|
||||
> 贪心法:
|
||||
```python
|
||||
class Solution:
|
||||
|
|
@ -276,6 +277,24 @@ func findLengthOfLCIS(nums []int) int {
|
|||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
pub fn find_length_of_lcis(nums: Vec<i32>) -> i32 {
|
||||
if nums.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let mut result = 1;
|
||||
let mut dp = vec![1; nums.len()];
|
||||
for i in 1..nums.len() {
|
||||
if nums[i - 1] < nums[i] {
|
||||
dp[i] = dp[i - 1] + 1;
|
||||
result = result.max(dp[i]);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
|
||||
> 动态规划:
|
||||
|
|
@ -319,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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ class Solution:
|
|||
root.right = self.insertIntoBST(root.right, val)
|
||||
|
||||
# 返回更新后的以当前root为根节点的新树
|
||||
return roo
|
||||
return root
|
||||
```
|
||||
|
||||
**递归法** - 无返回值
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
|
||||
## 思路
|
||||
|
||||
为了易于大家理解,我还录制了视频,可以看这里:[手把手带你撕出正确的二分法](https://www.bilibili.com/video/BV1fA4y1o715)
|
||||
|
||||
**这道题目的前提是数组为有序数组**,同时题目还强调**数组中无重复元素**,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
|
||||
|
||||
二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 `while(left < right)` 还是 `while(left <= right)`,到底是`right = middle`呢,还是要`right = middle - 1`呢?
|
||||
|
|
@ -612,5 +614,81 @@ public class Solution{
|
|||
```
|
||||
|
||||
|
||||
|
||||
**Kotlin:**
|
||||
```Kotlin
|
||||
// (版本一)左闭右开区间
|
||||
class Solution {
|
||||
fun search(nums: IntArray, target: Int): Int {
|
||||
var left = 0
|
||||
var right = nums.size // [left,right) 右侧为开区间,right 设置为 nums.size
|
||||
while (left < right) {
|
||||
val mid = (left + right) / 2
|
||||
if (nums[mid] < target) left = mid + 1
|
||||
else if (nums[mid] > target) right = mid // 代码的核心,循环中 right 是开区间,这里也应是开区间
|
||||
else return mid
|
||||
}
|
||||
return -1 // 没有找到 target ,返回 -1
|
||||
}
|
||||
}
|
||||
// (版本二)左闭右闭区间
|
||||
class Solution {
|
||||
fun search(nums: IntArray, target: Int): Int {
|
||||
var left = 0
|
||||
var right = nums.size - 1 // [left,right] 右侧为闭区间,right 设置为 nums.size - 1
|
||||
while (left <= right) {
|
||||
val mid = (left + right) / 2
|
||||
if (nums[mid] < target) left = mid + 1
|
||||
else if (nums[mid] > target) right = mid - 1 // 代码的核心,循环中 right 是闭区间,这里也应是闭区间
|
||||
else return mid
|
||||
}
|
||||
return -1 // 没有找到 target ,返回 -1
|
||||
}
|
||||
}
|
||||
```
|
||||
**Scala:**
|
||||
|
||||
(版本一)左闭右闭区间
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -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()]){
|
||||
|
|
@ -233,7 +234,7 @@ class Solution {
|
|||
}
|
||||
```
|
||||
Python:
|
||||
``` Python3
|
||||
```python
|
||||
class Solution:
|
||||
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
|
||||
answer = [0]*len(temperatures)
|
||||
|
|
@ -277,8 +278,36 @@ func dailyTemperatures(t []int) []int {
|
|||
}
|
||||
```
|
||||
|
||||
> 单调栈法
|
||||
> 单调栈法(未精简版本)
|
||||
|
||||
```go
|
||||
func dailyTemperatures(temperatures []int) []int {
|
||||
res := make([]int, len(temperatures))
|
||||
// 初始化栈顶元素为第一个下标索引0
|
||||
stack := []int{0}
|
||||
|
||||
for i := 1; i < len(temperatures); i++ {
|
||||
top := stack[len(stack)-1]
|
||||
if temperatures[i] < temperatures[top] {
|
||||
stack = append(stack, i)
|
||||
} else if temperatures[i] == temperatures[top] {
|
||||
stack = append(stack, i)
|
||||
} else {
|
||||
for len(stack) != 0 && temperatures[i] > temperatures[top] {
|
||||
res[top] = i - top
|
||||
stack = stack[:len(stack)-1]
|
||||
if len(stack) != 0 {
|
||||
top = stack[len(stack)-1]
|
||||
}
|
||||
}
|
||||
stack = append(stack, i)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
> 单调栈法(精简版本)
|
||||
```go
|
||||
// 单调递减栈
|
||||
func dailyTemperatures(num []int) []int {
|
||||
|
|
@ -343,6 +372,32 @@ var dailyTemperatures = function(temperatures) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 精简版:
|
||||
|
||||
```typescript
|
||||
function dailyTemperatures(temperatures: number[]): number[] {
|
||||
const length: number = temperatures.length;
|
||||
const stack: number[] = [];
|
||||
const resArr: number[] = new Array(length).fill(0);
|
||||
stack.push(0);
|
||||
for (let i = 1; i < length; i++) {
|
||||
let top = stack[stack.length - 1];
|
||||
while (
|
||||
stack.length > 0 &&
|
||||
temperatures[top] < temperatures[i]
|
||||
) {
|
||||
resArr[top] = i - top;
|
||||
stack.pop();
|
||||
top = stack[stack.length - 1];
|
||||
}
|
||||
stack.push(i);
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,30 @@ var minCostClimbingStairs = function(cost) {
|
|||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function minCostClimbingStairs(cost: number[]): number {
|
||||
/**
|
||||
dp[i]: 走到第i阶需要花费的最少金钱
|
||||
dp[0]: cost[0];
|
||||
dp[1]: cost[1];
|
||||
...
|
||||
dp[i]: min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
*/
|
||||
const dp: number[] = [];
|
||||
const length: number = cost.length;
|
||||
dp[0] = cost[0];
|
||||
dp[1] = cost[1];
|
||||
for (let i = 2; i <= length; i++) {
|
||||
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
return Math.min(dp[length - 1], dp[length - 2]);
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
int minCostClimbingStairs(int* cost, int costSize){
|
||||
//开辟dp数组,大小为costSize
|
||||
|
|
|
|||
|
|
@ -158,6 +158,71 @@ class Solution {
|
|||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
class Solution{
|
||||
/*解法二: 上述c++补充思路的Java代码实现*/
|
||||
|
||||
public int[][] findPartitions(String s) {
|
||||
List<Integer> temp = new ArrayList<>();
|
||||
int[][] hash = new int[26][2];//26个字母2列 表示该字母对应的区间
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
//更新字符c对应的位置i
|
||||
char c = s.charAt(i);
|
||||
if (hash[c - 'a'][0] == 0) hash[c - 'a'][0] = i;
|
||||
|
||||
hash[c - 'a'][1] = i;
|
||||
|
||||
//第一个元素区别对待一下
|
||||
hash[s.charAt(0) - 'a'][0] = 0;
|
||||
}
|
||||
|
||||
|
||||
List<List<Integer>> h = new LinkedList<>();
|
||||
//组装区间
|
||||
for (int i = 0; i < 26; i++) {
|
||||
//if (hash[i][0] != hash[i][1]) {
|
||||
temp.clear();
|
||||
temp.add(hash[i][0]);
|
||||
temp.add(hash[i][1]);
|
||||
//System.out.println(temp);
|
||||
h.add(new ArrayList<>(temp));
|
||||
// }
|
||||
}
|
||||
// System.out.println(h);
|
||||
// System.out.println(h.size());
|
||||
int[][] res = new int[h.size()][2];
|
||||
for (int i = 0; i < h.size(); i++) {
|
||||
List<Integer> list = h.get(i);
|
||||
res[i][0] = list.get(0);
|
||||
res[i][1] = list.get(1);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
public List<Integer> partitionLabels(String s) {
|
||||
int[][] partitions = findPartitions(s);
|
||||
List<Integer> res = new ArrayList<>();
|
||||
Arrays.sort(partitions, (o1, o2) -> Integer.compare(o1[0], o2[0]));
|
||||
int right = partitions[0][1];
|
||||
int left = 0;
|
||||
for (int i = 0; i < partitions.length; i++) {
|
||||
if (partitions[i][0] > right) {
|
||||
//左边界大于右边界即可纪委一次分割
|
||||
res.add(right - left + 1);
|
||||
left = partitions[i][0];
|
||||
}
|
||||
right = Math.max(right, partitions[i][1]);
|
||||
|
||||
}
|
||||
//最右端
|
||||
res.add(right - left + 1);
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
# 思路
|
||||
|
||||
为了易于大家理解,我还特意录制了视频,[本题视频讲解](https://www.bilibili.com/video/BV1QB4y1D7ep)
|
||||
|
||||
## 暴力排序
|
||||
|
||||
最直观的想法,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
|
||||
|
|
@ -359,6 +361,64 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
class Solution {
|
||||
// 双指针法
|
||||
fun sortedSquares(nums: IntArray): IntArray {
|
||||
var res = IntArray(nums.size)
|
||||
var left = 0 // 指向数组的最左端
|
||||
var right = nums.size - 1 // 指向数组端最右端
|
||||
// 选择平方数更大的那一个往 res 数组中倒序填充
|
||||
for (index in nums.size - 1 downTo 0) {
|
||||
if (nums[left] * nums[left] > nums[right] * nums[right]) {
|
||||
res[index] = nums[left] * nums[left]
|
||||
left++
|
||||
} else {
|
||||
res[index] = nums[right] * nums[right]
|
||||
right--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scala:
|
||||
|
||||
双指针:
|
||||
```scala
|
||||
object Solution {
|
||||
def sortedSquares(nums: Array[Int]): Array[Int] = {
|
||||
val res: Array[Int] = new Array[Int](nums.length)
|
||||
var top = nums.length - 1
|
||||
var i = 0
|
||||
var j = nums.length - 1
|
||||
while (i <= j) {
|
||||
if (nums(i) * nums(i) <= nums(j) * nums(j)) {
|
||||
// 当左侧平方小于等于右侧,res数组顶部放右侧的平方,并且top下移,j左移
|
||||
res(top) = nums(j) * nums(j)
|
||||
top -= 1
|
||||
j -= 1
|
||||
} else {
|
||||
// 当左侧平方大于右侧,res数组顶部放左侧的平方,并且top下移,i右移
|
||||
res(top) = nums(i) * nums(i)
|
||||
top -= 1
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
```
|
||||
骚操作(暴力思路):
|
||||
```scala
|
||||
object Solution {
|
||||
def sortedSquares(nums: Array[Int]): Array[Int] = {
|
||||
nums.map(x=>{x*x}).sortWith(_ < _)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue