Merge pull request #789 from jerryfishcode/master

添加 5. 31. 34. 42. 52. 70(完全背包版本). 84. 127. 129. 132. 141. 143. 205. 234. 283. 463. 649. 673. 684. 685. 724. 841. 844. 922. 925. 941. 1207. 1221. 1356. 1365. 1382.JavaScript版本
This commit is contained in:
程序员Carl
2021-09-28 12:37:36 +08:00
committed by GitHub
31 changed files with 1228 additions and 3 deletions

View File

@ -297,6 +297,117 @@ class Solution:
## JavaScript
```js
//动态规划解法
var longestPalindrome = function(s) {
const len = s.length;
// 布尔类型的dp[i][j]:表示区间范围[i,j] 注意是左闭右闭的子串是否是回文子串如果是dp[i][j]为true否则为false
let dp = new Array(len).fill(false).map(() => new Array(len).fill(false));
// left起始位置 maxlenth回文串长度
let left = 0, maxlenth = 0;
for(let i = len - 1; i >= 0; i--){
for(let j = i; j < len; j++){
// 情况一下标i 与 j相同同一个字符例如a当然是回文子串 j - i == 0
// 情况二下标i 与 j相差为1例如aa也是文子串 j - i == 1
// 情况一和情况二 可以合并为 j - i <= 1
// 情况三下标i 与 j相差大于1的时候例如cabac此时s[i]与s[j]已经相同了我们看i到j区间是不是回文子串就看aba是不是回文就可以了那么aba的区间就是 i+1 与 j-1区间这个区间是不是回文就看dp[i + 1][j - 1]===true
if(s[i] === s[j] && (j - i <= 1 || dp[i + 1][j - 1])){
dp[i][j] = true;
}
// 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
if(dp[i][j] && j - i + 1 > maxlenth) {
maxlenth = j - i + 1; // 回文串长度
left = i; // 起始位置
}
}
}
return s.substr(left, maxlenth); // 找到子串
};
//双指针
var longestPalindrome = function(s) {
let left = 0, right = 0, maxLength = 0;
const extend = (s, i, j, n) => {// s为字符串 i,j为双指针 n为字符串长度
while(i >= 0 && j < n && s[i] === s[j]){
if(j - i + 1 > maxLength){
left = i; // 更新开始位置
right = j; // 更新结尾位置
maxLength = j - i + 1; // 更新子串最大长度
}
// 指针移动
i--;
j++;
}
}
for(let i = 0; i < s.length; i++){
extend(s, i, i, s.length); // 以i为中心
extend(s, i, i + 1, s.length); // 以i和i+1为中心
}
return s.substr(left, maxLength);
};
//Manacher算法
var longestPalindrome = function(s) {
const len = s.length;
if(len < 2) return s;
let maxLength = 1, index = 0;
//Manacher算法,利用回文对称的性质根据i在上一个回文中心的臂长里的位置去判断i的回文性
//需要知道上一个回文中心,以及其臂长
let center = 0;
//注意这里使用了maxRight的而不是真实的臂长length,因为之后需要判断i在臂长的什么位置
//如果这里臂长用了length,之后还要 计算i - center 去和 length比较太繁琐
let maxRight = 0;
//考虑到回文串的长度是偶数的情况,所以这里预处理一下字符串,每个字符间插入特殊字符,把可能性都化为奇数
//这个处理把回文串长度的可能性都化为了奇数
//#c#b#b#a#
//#c#b#a#b#d#
let ss = "";
for(let i = 0; i < s.length; i++){
ss += "#"+s[i];
}
ss += "#";
//需要维护一个每个位置臂长的信息数组positionLength
const pl = new Array(ss.length).fill(0);
//这里需要注意参考的是i关于center对称的点i'的回文性
//i' = 2*center - i;
//所以列下情况:
//1.i>maxRight,找不到i',无法参考,自己算自己的
//2.i<=maxRight:
//2.1 i<maxRight-pl[i'],pl[i']的臂长没有超过center的臂长,根据对称性,pl[i] = pl[i']
//2.2 i=maxRight-pl[i'],pl[i']的臂长刚好等于center的臂长,根据对称性,pl[i] >= pl[i],大多少需要尝试扩散
//2.3 i>maxRight-pl[i'],pl[i']的臂长超过了center的臂长,根据对称性,i中心扩散到MaxRight处,
// s[2*i-maxRight] !== s[MaxRight]必不相等所以pl[i] = maxRight-i;
//总结就是pl[i] = Math.min(maxRight-i,pl[i']);提示i<maxRight-pl[i'] 也可写成 pl[i']<maxRight-i
//0没有意义,从1开始计算
for(let i = 1; i < ss.length; i++){
if(i <= maxRight){//可以参考之前的
pl[i] = Math.min(maxRight - i, pl[2 * center - i]);
//尝试中心扩散
}
//注意到i<maxRight时都要尝试中心扩散,所以写else完全无意义,把中心扩散的代码写在下面
// else{//i不在之前回文中心的臂长范围里,之前的信息就完全无法参考,只能从i中心扩散把然后去维护maxRight和center的定义
//尝试中心扩散
//这里不要动center和maxRight
// center = i;
// maxRight = pl[i] + i + 1;
let right = pl[i] + i + 1;
let left = i - pl[i] - 1;
while (left >= 0 && right<ss.length && ss[left] === ss[right]) {
right++;
left--;
pl[i]++;
}
// }
if(pl[i] + i > maxRight){
center = i;
maxRight = pl[i] + i;
}
if (pl[i] * 2 + 1 > maxLength){
maxLength = pl[i]*2+1;
index = i - pl[i];
}
}
return ss.substr(index, maxLength).replace(/#/g,"");
};
```
-----------------------

View File

@ -132,6 +132,52 @@ class Solution {
## JavaScript
```js
//卡尔的解法(吐槽一下JavaScript的sort和其他语言的不太一样只想到了拷贝数组去排序再替换原数组来实现nums的[i + 1, nums.length)升序排序)
var nextPermutation = function(nums) {
for(let i = nums.length - 1; i >= 0; i--){
for(let j = nums.length - 1; j > i; j--){
if(nums[j] > nums[i]){
[nums[j],nums[i]] = [nums[i],nums[j]]; // 交换
// 深拷贝[i + 1, nums.length)部分到新数组arr
let arr = nums.slice(i+1);
// arr升序排序
arr.sort((a,b) => a - b);
// arr替换nums的[i + 1, nums.length)部分
nums.splice(i+1,nums.length - i, ...arr);
return;
}
}
}
nums.sort((a,b) => a - b); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
};
//另一种
var nextPermutation = function(nums) {
let i = nums.length - 2;
// 从右往左遍历拿到第一个左边小于右边的 i,此时 i 右边的数组是从右往左递增的
while (i >= 0 && nums[i] >= nums[i+1]){
i--;
}
if (i >= 0){
let j = nums.length - 1;
// 从右往左遍历拿到第一个大于nums[i]的数,因为之前nums[i]是第一个小于他右边的数,所以他的右边一定有大于他的数
while (j >= 0 && nums[j] <= nums[i]){
j--;
}
// 交换两个数
[nums[j], nums[i]] = [nums[i], nums[j]];
}
// 对 i 右边的数进行交换
// 因为 i 右边的数原来是从右往左递增的,把一个较小的值交换过来之后,仍然维持单调递增特性
// 此时头尾交换并向中间逼近就能获得 i 右边序列的最小值
let l = i + 1;
let r = nums.length - 1;
while (l < r){
[nums[l], nums[r]] = [nums[r], nums[l]];
l++;
r--;
}
};
```
-----------------------

View File

@ -396,6 +396,46 @@ class Solution:
## JavaScript
```js
var searchRange = function(nums, target) {
const getLeftBorder = (nums, target) => {
let left = 0, right = nums.length - 1;
let leftBorder = -2;// 记录一下leftBorder没有被赋值的情况
while(left <= right){
let middle = left + ((right - left) >> 1);
if(nums[middle] >= target){ // 寻找左边界nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
const getRightBorder = (nums, target) => {
let left = 0, right = nums.length - 1;
let rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
let middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界nums[middle] == target的时候更新left
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
let leftBorder = getLeftBorder(nums, target);
let rightBorder = getRightBorder(nums, target);
// 情况一
if(leftBorder === -2 || rightBorder === -2) return [-1,-1];
// 情况三
if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];
// 情况二
return [-1, -1];
};
```
-----------------------

View File

@ -534,6 +534,105 @@ class Solution:
Go:
JavaScript:
```javascript
//双指针
var trap = function(height) {
const len = height.length;
let sum = 0;
for(let i = 0; i < len; i++){
// 第一个柱子和最后一个柱子不接雨水
if(i == 0 || i == len - 1) continue;
let rHeight = height[i]; // 记录右边柱子的最高高度
let lHeight = height[i]; // 记录左边柱子的最高高度
for(let r = i + 1; r < len; r++){
if(height[r] > rHeight) rHeight = height[r];
}
for(let l = i - 1; l >= 0; l--){
if(height[l] > lHeight) lHeight = height[l];
}
let h = Math.min(lHeight, rHeight) - height[i];
if(h > 0) sum += h;
}
return sum;
};
//动态规划
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0;
const maxLeft = new Array(len).fill(0);
const maxRight = new Array(len).fill(0);
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for(let i = 1; i < len; i++){
maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[len - 1] = height[len - 1];
for(let i = len - 2; i >= 0; i--){
maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
// 求和
let sum = 0;
for(let i = 0; i < len; i++){
let count = Math.min(maxLeft[i], maxRight[i]) - height[i];
if(count > 0) sum += count;
}
return sum;
};
//单调栈 js数组作为栈
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0; // 可以不加
const st = [];// 存着下标,计算的时候用下标对应的柱子高度
st.push(0);
let sum = 0;
for(let i = 1; i < len; i++){
if(height[i] < height[st[st.length - 1]]){ // 情况一
st.push(i);
}
if (height[i] == height[st[st.length - 1]]) { // 情况二
st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
st.push(i);
} else { // 情况三
while (st.length !== 0 && height[i] > height[st[st.length - 1]]) { // 注意这里是while
let mid = st[st.length - 1];
st.pop();
if (st.length !== 0) {
let h = Math.min(height[st[st.length - 1]], height[i]) - height[mid];
let w = i - st[st.length - 1] - 1; // 注意减一,只求中间宽度
sum += h * w;
}
}
st.push(i);
}
}
return sum;
};
//单调栈 简洁版本 只处理情况三
var trap = function(height) {
const len = height.length;
if(len <= 2) return 0; // 可以不加
const st = [];// 存着下标,计算的时候用下标对应的柱子高度
st.push(0);
let sum = 0;
for(let i = 1; i < len; i++){ // 只处理的情况三,其实是把情况一和情况二融合了
while (st.length !== 0 && height[i] > height[st[st.length - 1]]) { // 注意这里是while
let mid = st[st.length - 1];
st.pop();
if (st.length !== 0) {
let h = Math.min(height[st[st.length - 1]], height[i]) - height[mid];
let w = i - st[st.length - 1] - 1; // 注意减一,只求中间宽度
sum += h * w;
}
}
st.push(i);
}
return sum;
};
```
C:

View File

@ -101,4 +101,48 @@ public:
```
# 其他语言补充
JavaScript
```javascript
var totalNQueens = function(n) {
let count = 0;
const backtracking = (n, row, chessboard) => {
if(row === n){
count++;
return;
}
for(let col = 0; col < n; col++){
if(isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯
}
}
}
const isValid = (row, col, chessboard, n) => {
// 检查列
for(let i = 0; i < row; i++){ // 这是一个剪枝
if(chessboard[i][col] === 'Q'){
return false;
}
}
// 检查 45度角是否有皇后
for(let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
if(chessboard[i][j] === 'Q'){
return false;
}
}
// 检查 135度角是否有皇后
for(let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){
if(chessboard[i][j] === 'Q'){
return false;
}
}
return true;
}
const chessboard = new Array(n).fill([]).map(() => new Array(n).fill('.'));
backtracking(n, 0, chessboard);
return count;
};
```

View File

@ -186,6 +186,20 @@ func climbStairs(n int) int {
}
```
JavaScript:
```javascript
var climbStairs = function(n) {
const dp = new Array(n+1).fill(0);
const weight = [1,2];
dp[0] = 1;
for(let i = 0; i <= n; i++){ //先遍历背包
for(let j = 0; j < weight.length; j++){ // 再遍历物品
if(i >= weight[j]) dp[i] += dp[i-weight[j]];
}
}
return dp[n];
};
```
-----------------------

View File

@ -321,4 +321,79 @@ class Solution:
return result
```
JavaScript:
```javascript
//动态规划 js中运行速度最快
var largestRectangleArea = function(heights) {
const len = heights.length;
const minLeftIndex = new Array(len);
const maxRigthIndex = new Array(len);
// 记录每个柱子 左边第一个小于该柱子的下标
minLeftIndex[0] = -1; // 注意这里初始化防止下面while死循环
for(let i = 1; i < len; i++) {
let t = i - 1;
// 这里不是用if而是不断向左寻找的过程
while(t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
// 记录每个柱子 右边第一个小于该柱子的下标
maxRigthIndex[len - 1] = len; // 注意这里初始化防止下面while死循环
for(let i = len - 2; i >= 0; i--){
let t = i + 1;
// 这里不是用if而是不断向右寻找的过程
while(t < len && heights[t] >= heights[i]) t = maxRigthIndex[t];
maxRigthIndex[i] = t;
}
// 求和
let maxArea = 0;
for(let i = 0; i < len; i++){
let sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1);
maxArea = Math.max(maxArea , sum);
}
return maxArea;
};
//单调栈
var largestRectangleArea = function(heights) {
let maxArea = 0;
const stack = [];
heights = [0,...heights,0]; // 数组头部加入元素0 数组尾部加入元素0
for(let i = 0; i < heights.length; i++){
if(heights[i] > heights[stack[stack.length-1]]){ // 情况三
stack.push(i);
} else if(heights[i] === heights[stack[stack.length-1]]){ // 情况二
stack.pop(); // 这个可以加,可以不加,效果一样,思路不同
stack.push(i);
} else { // 情况一
while(heights[i] < heights[stack[stack.length-1]]){// 当前bar比栈顶bar矮
const stackTopIndex = stack.pop();// 栈顶元素出栈并保存栈顶bar的索引
let w = i - stack[stack.length -1] - 1;
let h = heights[stackTopIndex]
// 计算面积,并取最大面积
maxArea = Math.max(maxArea, w * h);
}
stack.push(i);// 当前bar比栈顶bar高了入栈
}
}
return maxArea;
};
//单调栈 简洁
var largestRectangleArea = function(heights) {
let maxArea = 0;
const stack = [];
heights = [0,...heights,0]; // 数组头部加入元素0 数组尾部加入元素0
for(let i = 0; i < heights.length; i++){ // 只用考虑情况一 当前遍历的元素heights[i]小于栈顶元素heights[stack[stack.length-1]]]的情况
while(heights[i] < heights[stack[stack.length-1]]){// 当前bar比栈顶bar矮
const stackTopIndex = stack.pop();// 栈顶元素出栈并保存栈顶bar的索引
let w = i - stack[stack.length -1] - 1;
let h = heights[stackTopIndex]
// 计算面积,并取最大面积
maxArea = Math.max(maxArea, w * h);
}
stack.push(i);// 当前bar比栈顶bar高了入栈
}
return maxArea;
};
```
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>

View File

@ -141,6 +141,40 @@ public int ladderLength(String beginWord, String endWord, List<String> wordList)
## Go
## JavaScript
```javascript
var ladderLength = function(beginWord, endWord, wordList) {
// 将wordList转成Set提高查询速度
const wordSet = new Set(wordList);
// Set元素个数为0 或者 endWord没有在wordSet出现直接返回0
if (wordSet.size === 0 || !wordSet.has(endWord)) return 0;
// 记录word是否访问过
const visitMap = new Map();// <word, 查询到这个word路径长度>
// 初始化队列
const queue = [];
queue.push(beginWord);
// 初始化visitMap
visitMap.set(beginWord, 1);
while(queue.length !== 0){
let word = queue.shift(); // 删除队首元素,将它的值存放在word
let path = visitMap.get(word); // 这个word的路径长度
for(let i = 0; i < word.length; i++){ // 遍历单词的每个字符
for (let c = 97; c <= 122; c++) { // 对应26个字母ASCII值 从'a' 到 'z' 遍历替换
// 拼串得到新的字符串
let newWord = word.slice(0, i) + String.fromCharCode(c) + word.slice(i + 1);
if(newWord === endWord) return path + 1; // 找到了end返回path+1
// wordSet出现了newWord并且newWord没有被访问过
if(wordSet.has(newWord) && !visitMap.has(newWord)) {
// 添加访问信息
visitMap.set(newWord, path + 1);
queue.push(newWord);
}
}
}
}
return 0;
};
```
-----------------------

View File

@ -241,6 +241,48 @@ class Solution:
Go
JavaScript
```javascript
var sumNumbers = function(root) {
const listToInt = path => {
let sum = 0;
for(let num of path){
// sum * 10 表示进位
sum = sum * 10 + num;
}
return sum;
}
const recur = root =>{
if (root.left == null && root.right == null) {
// 当是叶子节点的时候,开始处理
res += listToInt(path);
return;
}
if (root.left != null){
// 注意有回溯
path.push(root.left.val);
recur(root.left);
path.pop();
}
if (root.right != null){
// 注意有回溯
path.push(root.right.val);
recur(root.right);
path.pop();
}
return;
};
const path = new Array();
let res = 0;
// 如果节点为0那么就返回0
if (root == null) return 0;
// 首先将根节点放到集合中
path.push(root.val);
// 开始递归
recur(root);
return res;
};
```

View File

@ -247,6 +247,42 @@ class Solution:
## JavaScript
```js
var minCut = function(s) {
const len = s.length;
// 二维数组isPalindromic来保存整个字符串的回文情况
const isPalindromic = new Array(len).fill(false).map(() => new Array(len).fill(false));
for(let i = len - 1; i >= 0; i--){
for(let j = i; j < len; j++){
if(s[i] === s[j] && (j - i <= 1 || isPalindromic[i + 1][j - 1])){
isPalindromic[i][j] = true;
}
}
}
// dp[i]:范围是[0, i]的回文子串最少分割次数是dp[i]
const dp = new Array(len).fill(0);
for(let i = 0; i < len; i++) dp[i] = i; // 初始化 dp[i]的最大值其实就是i也就是把每个字符分割出来
for(let i = 1; i < len; i++){
if(isPalindromic[0][i]){ // 判断是不是回文子串
dp[i] = 0;
continue;
}
/*
如果要对长度为[0, i]的子串进行分割分割点为j。
那么如果分割后,区间[j + 1, i]是回文子串那么dp[i] 就等于 dp[j] + 1。
这里可能有同学就不明白了,为什么只看[j + 1, i]区间,不看[0, j]区间是不是回文子串呢?
那么在回顾一下dp[i]的定义: 范围是[0, i]的回文子串最少分割次数是dp[i]。
[0, j]区间的最小切割数量我们已经知道了就是dp[j]。
此时就找到了递推关系当切割点j在[0, i] 之间时候dp[i] = dp[j] + 1;
本题是要找到最少分割次数所以遍历j的时候要取最小的dp[i]。dp[i] = Math.min(dp[i], dp[j] + 1);
*/
for(let j = 0; j < i; j++){
if(isPalindromic[j + 1][i]){
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[len - 1];
};
```
-----------------------

View File

@ -114,6 +114,17 @@ class Solution:
## JavaScript
```js
var hasCycle = function(head) {
let fast = head;
let slow = head;
// 空链表、单节点链表一定不会有环
while(fast != null && fast.next != null){
fast = fast.next.next; // 快指针,一次移动两步
slow = slow.next; // 慢指针,一次移动一步
if(fast === slow) return true; // 快慢指针相遇,表明有环
}
return false; // 正常走到链表末尾,表明没有环
};
```
-----------------------

View File

@ -351,6 +351,110 @@ class Solution:
Go
JavaScript
```javascript
// 方法一 使用数组存储节点
var reorderList = function(head, s = [], tmp) {
let cur = head;
// list是数组可以使用下标随机访问
const list = [];
while(cur != null){
list.push(cur);
cur = cur.next;
}
cur = head; // 重新回到头部
let l = 1, r = list.length - 1; // 注意左边是从1开始
let count = 0;
while(l <= r){
if(count % 2 == 0){
// even
cur.next = list[r];
r--;
} else {
// odd
cur.next = list[l];
l++;
}
// 每一次指针都需要移动
cur = cur.next;
count++;
}
// 当是偶数的话,需要做额外处理
if(list.length % 2 == 0){
cur.next = list[l];
cur = cur.next;
}
// 注意结尾要结束一波
cur.next = null;
}
// 方法二 使用双端队列的方法来解决 js中运行很慢
var reorderList = function(head, s = [], tmp) {
// js数组作为双端队列
const deque = [];
// 这里是取head的下一个节点head不需要再入队了避免造成重复
let cur = head.next;
while(cur != null){
deque.push(cur);
cur = cur.next;
}
cur = head; // 回到头部
let count = 0;
while(deque.length !== 0){
if(count % 2 == 0){
// even取出队列右边尾部的值
cur.next = deque.pop();
} else {
// odd, 取出队列左边头部的值
cur.next = deque.shift();
}
cur = cur.next;
count++;
}
cur.next = null;
}
//方法三 将链表分割成两个链表,然后把第二个链表反转,之后在通过两个链表拼接成新的链表
var reorderList = function(head, s = [], tmp) {
const reverseList = head => {
let headNode = new ListNode(0);
let cur = head;
let next = null;
while(cur != null){
next = cur.next;
cur.next = headNode.next;
headNode.next = cur;
cur = next;
}
return headNode.next;
}
let fast = head, slow = head;
//求出中点
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
//right就是右半部分 12345 就是45 1234 就是34
let right = slow.next;
//断开左部分和右部分
slow.next = null;
//反转右部分 right就是反转后右部分的起点
right = reverseList(right);
//左部分的起点
let left = head;
//进行左右部分来回连接
//这里左部分的节点个数一定大于等于右部分的节点个数 因此只判断right即可
while (right != null) {
let curLeft = left.next;
left.next = right;
left = curLeft;
let curRight = right.next;
right.next = left;
right = curRight;
}
}
```
-----------------------

View File

@ -119,6 +119,25 @@ func isIsomorphic(s string, t string) bool {
## JavaScript
```js
var isIsomorphic = function(s, t) {
let len = s.length;
if(len === 0) return true;
let maps = new Map();
let mapt = new Map();
for(let i = 0, j = 0; i < len; i++, j++){
if(!maps.has(s[i])){
maps.set(s[i],t[j]);// maps保存 s[i] 到 t[j]的映射
}
if(!mapt.has(t[j])){
mapt.set(t[j],s[i]);// mapt保存 t[j] 到 s[i]的映射
}
// 无法映射,返回 false
if(maps.get(s[i]) !== t[j] || mapt.get(t[j]) !== s[i]){
return false;
}
};
return true;
};
```
-----------------------

View File

@ -284,7 +284,41 @@ class Solution:
## JavaScript
```js
var isPalindrome = function(head) {
const reverseList = head => {// 反转链表
let temp = null;
let pre = null;
while(head != null){
temp = head.next;
head.next = pre;
pre = head;
head = temp;
}
return pre;
}
// 如果为空或者仅有一个节点返回true
if(!head && !head.next) return true;
let slow = head;
let fast = head;
let pre = head;
while(fast != null && fast.next != null){
pre = slow; // 记录slow的前一个结点
slow = slow.next;
fast = fast.next.next;
}
pre.next = null; // 分割两个链表
// 前半部分
let cur1 = head;
// 后半部分。这里使用了反转链表
let cur2 = reverseList(slow);
while(cur1 != null){
if(cur1.val != cur2.val) return false;
// 注意要移动两个结点
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
};
```

View File

@ -95,6 +95,21 @@ Python
Go
JavaScript
```javascript
var moveZeroes = function(nums) {
let slow = 0;
for(let fast = 0; fast < nums.length; fast++){
if(nums[fast] != 0){//找到非0的元素
nums[slow] = nums[fast];//把非0的元素赋值给数组慢指针指向的索引处的值
slow++;//慢指针向右移动
}
}
// 后面的元素全变成 0
for(let j = slow; j < nums.length; j++){
nums[j] = 0;
}
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -120,6 +120,55 @@ Python
Go
JavaScript
```javascript
//解法一
var islandPerimeter = function(grid) {
// 上下左右 4 个方向
const dirx = [-1, 1, 0, 0], diry = [0, 0, -1, 1];
const m = grid.length, n = grid[0].length;
let res = 0; //岛屿周长
for(let i = 0; i < m; i++){
for(let j = 0; j < n; j++){
if(grid[i][j] === 1){
for(let k = 0; k < 4; k++){ //上下左右四个方向
// 计算周边坐标的x,y
let x = i + dirx[k];
let y = j + diry[k];
// 四个方向扩展的新位置是水域或者越界就会为周长贡献1
if(x < 0 // i在边界上
|| x >= m // i在边界上
|| y < 0 // j在边界上
|| y >= n // j在边界上
|| grid[x][y] === 0){ // (x,y)位置是水域
res++;
continue;
}
}
}
}
}
return res;
};
//解法二
var islandPerimeter = function(grid) {
let sum = 0; // 陆地数量
let cover = 0; // 相邻数量
for(let i = 0; i < grid.length; i++){
for(let j = 0; j <grid[0].length; j++){
if(grid[i][j] === 1){
sum++;
// 统计上边相邻陆地
if(i - 1 >= 0 && grid[i-1][j] === 1) cover++;
// 统计左边相邻陆地
if(j - 1 >= 0 && grid[i][j-1] === 1) cover++;
// 为什么没统计下边和右边? 因为避免重复计算
}
}
}
return sum * 4 - cover * 2;
};
```
-----------------------

View File

@ -219,6 +219,31 @@ func predictPartyVictory(senateStr string) string {
## JavaScript
```js
var predictPartyVictory = function(senateStr) {
// R = true表示本轮循环结束后字符串里依然有R;D同理。
let R = true, D = true;
// 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
let flag = 0;
let senate = senateStr.split('');
while(R && D){ // 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = false;
D = false;
for(let i = 0; i < senate.length; i++){
if(senate[i] === 'R'){
if(flag < 0) senate[i] = 0;// 消灭RR此时为false
else R = true;// 如果没被消灭本轮循环结束有R
flag++;
}
if(senate[i] === 'D'){
if(flag > 0) senate[i] = 0;
else D = true;
flag--;
}
}
}
// 循环结束之后R和D只能有一个为true
return R ? "Radiant" : "Dire";
};
```
-----------------------

View File

@ -337,6 +337,28 @@ func findNumberOfLIS(nums []int) int {
## JavaScript
```js
var findNumberOfLIS = function(nums) {
const len = nums.length;
if(len <= 1) return len;
let dp = new Array(len).fill(1); // i之前包括i最长递增子序列的长度为dp[i]
let count = new Array(len).fill(1); // 以nums[i]为结尾的字符串最长递增子序列的个数为count[i]
let res = 0;
for(let i = 1; i < len; i++){
for(let j = 0; j < i; j++){
if(nums[i] > nums[j]){
if(dp[j] + 1 > dp[i]){ // 第 j 个数字为前一个数字的子序列是否更更长
dp[i] = dp[j] + 1; //更新 dp[i]
count[i] = count[j]; // 重置count[i]
} else if(dp[j] + 1 === dp[i]){ // 和原来一样长
count[i] += count[j]; //更新count[i]
}
}
}
}
let max = Math.max(...dp); //扩展运算符找到最大长度
for(let i = 0; i < len; i++) if(dp[i] === max) res += count[i]; // 累加
return res;
};
```
-----------------------

View File

@ -303,6 +303,43 @@ func findRedundantConnection(edges [][]int) []int {
## JavaScript
```js
const n = 1005;
const father = new Array(n);
// 并查集里寻根的过程
const find = u => {
return u == father[u] ? u : father[u] = find(father[u]);
};
// 将v->u 这条边加入并查集
const join = (u, v) => {
u = find(u);
v = find(v);
if(u == v) return;
father[v] = u;
};
// 判断 u 和 v是否找到同一个根本题用不上
const same = (u, v) => {
u = find(u);
v = find(v);
return u == v;
};
/**
* @param {number[][]} edges
* @return {number[]}
*/
var findRedundantConnection = function(edges) {
// 并查集初始化
for(let i = 0; i < n; i++){
father[i] = i;
}
for(let i = 0; i < edges.length; i++){
if(same(edges[i][0], edges[i][1])) return edges[i];
else join(edges[i][0], edges[i][1]);
}
return null;
};
```
-----------------------

View File

@ -515,6 +515,91 @@ func findRedundantDirectedConnection(edges [][]int) []int {
## JavaScript
```js
const N = 1010; // 如题二维数组大小的在3到1000范围内
const father = new Array(N);
let n; // 边的数量
// 并查集里寻根的过程
const find = u => {
return u == father[u] ? u : father[u] = find(father[u]);
};
// 将v->u 这条边加入并查集
const join = (u, v) => {
u = find(u);
v = find(v);
if(u == v) return;
father[v] = u;
};
// 判断 u 和 v是否找到同一个根
const same = (u, v) => {
u = find(u);
v = find(v);
return u == v;
};
// 在有向图里找到删除的那条边,使其变成树
const getRemoveEdge = edges => {
// 初始化并查集
for (let i = 1; i <= n; i++) {
father[i] = i;
}
for (let i = 0; i < n; i++) { // 遍历所有的边
if (same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
return edges[i];
}
join(edges[i][0], edges[i][1]);
}
return [];
}
// 删一条边之后判断是不是树
const isTreeAfterRemoveEdge = (edges, deleteEdge) => {
// 初始化并查集
for (let i = 1; i <= n; i++) {
father[i] = i;
}
for (let i = 0; i < n; i++) {
if (i == deleteEdge) continue;
if (same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树
return false;
}
join(edges[i][0], edges[i][1]);
}
return true;
}
/**
* @param {number[][]} edges
* @return {number[]}
*/
var findRedundantDirectedConnection = function(edges) {
n = edges.length;// 边的数量
const inDegree = new Array(n+1).fill(0); // 记录节点入度
for (let i = 0; i < n; i++) {
inDegree[edges[i][1]]++; // 统计入度
}
let vec = [];// 记录入度为2的边如果有的话就两条边
// 找入度为2的节点所对应的边注意要倒叙因为优先返回最后出现在二维数组中的答案
for (let i = n - 1; i >= 0; i--) {
if (inDegree[edges[i][1]] == 2) {
vec.push(i);
}
}
// 处理图中情况1 和 情况2
// 如果有入度为2的节点那么一定是两条边里删一个看删哪个可以构成树
if (vec.length > 0) {
if (isTreeAfterRemoveEdge(edges, vec[0])) {
return edges[vec[0]];
} else {
return edges[vec[1]];
}
}
// 处理图中情况3
// 明确没有入度为2的情况那么一定有有向环找到构成环的边返回就可以了
return getRemoveEdge(edges);
};
```
-----------------------

View File

@ -129,6 +129,17 @@ func pivotIndex(nums []int) int {
## JavaScript
```js
var pivotIndex = function(nums) {
const sum = nums.reduce((a,b) => a + b);//求和
// 中心索引左半和 中心索引右半和
let leftSum = 0, rightSum = 0;
for(let i = 0; i < nums.length; i++){
leftSum += nums[i];
rightSum = sum - leftSum + nums[i];// leftSum 里面已经有 nums[i],多减了一次,所以加上
if(leftSum === rightSum) return i;
}
return -1;
};
```
-----------------------

View File

@ -220,6 +220,55 @@ func canVisitAllRooms(rooms [][]int) bool {
```
JavaScript
```javascript
//DFS
var canVisitAllRooms = function(rooms) {
const dfs = (key, rooms, visited) => {
if(visited[key]) return;
visited[key] = 1;
for(let k of rooms[key]){
// 深度优先搜索遍历
dfs(k, rooms, visited);
}
}
const visited = new Array(rooms.length).fill(false);
dfs(0, rooms, visited);
//检查是否都访问到了
for (let i of visited) {
if (!i) {
return false;
}
}
return true;
};
//BFS
var canVisitAllRooms = function(rooms) {
const bfs = rooms => {
const visited = new Array(rooms.length).fill(0); // 标记房间是否被访问过
visited[0] = 1; // 0 号房间开始
const queue = []; //js数组作为队列使用
queue.push(0); // 0 号房间开始
// 广度优先搜索的过程
while(queue.length !== 0){
let key = queue[0];
queue.shift();
for(let k of rooms[key]){
if(!visited[k]){
queue.push(k);
visited[k] = 1;
}
}
}
// 检查房间是不是都遍历过了
for(let i of visited){
if(i === 0) return false;
}
return true;
}
return bfs(rooms);
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -232,6 +232,64 @@ func backspaceCompare(s string, t string) bool {
```
JavaScript
```javascript
// 双栈
var backspaceCompare = function(s, t) {
const arrS = [], arrT = []; // 数组作为栈使用
for(let char of s){
char === '#' ? arrS.pop() : arrS.push(char);
}
for(let char of t){
char === '#' ? arrT.pop() : arrT.push(char);
}
return arrS.join('') === arrT.join(''); // 比较两个字符串是否相等
};
//双栈精简
var backspaceCompare = function(s, t) {
const getString = s => {
let arrS = [];
for(let char of s){
char === '#' ? arrS.pop() : arrS.push(char);
}
return arrS.join('');
}
return getString(s) === getString(t);
};
//双指针
var backspaceCompare = function(s, t) {
let sSkipNum = 0; // 记录s的#数量
let tSkipNum = 0; // 记录t的#数量
let i = s.length - 1, j = t.length - 1;
while(true) {
while(i >= 0){ // 从后向前消除s的#
if(s[i] === '#') sSkipNum++;
else {
if (sSkipNum > 0) sSkipNum--;
else break;
}
i--;
}
while (j >= 0) { // 从后向前消除t的#
if (t[j] === '#') tSkipNum++;
else {
if (tSkipNum > 0) tSkipNum--;
else break;
}
j--;
}
// 后半部分#消除完了接下来比较s[i] != t[j]
if (i < 0 || j < 0) break; // s 或者t 遍历到头了
if (s[i] !== t[j]) return false;
i--;j--;
}
// 说明s和t同时遍历完毕
if (i == -1 && j == -1) return true;
return false;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -209,6 +209,57 @@ func sortArrayByParityII(nums []int) []int {
## JavaScript
```js
//方法一
var sortArrayByParityII = function(nums) {
const n = nums.length;
// 分别存放 nums 中的奇数、偶数
let evenIndex = 0, oddIndex = 0;
// 初始化就确定数组大小,节省开销
const even = new Array(Math.floor(n/2));
const odd = new Array(Math.floor(n/2));
// 把A数组放进偶数数组和奇数数组
for(let i = 0; i < n; i++){
if(nums[i] % 2 === 0) even[evenIndex++] = nums[i];
else odd[oddIndex++] = nums[i];
}
// 把奇偶数组重新存回 nums
let index = 0;
for(let i = 0; i < even.length; i++){
nums[index++] = even[i];
nums[index++] = odd[i];
}
return nums;
};
//方法二
var sortArrayByParityII = function(nums) {
const n = nums.length;
const result = new Array(n);
// 偶数下标 和 奇数下标
let evenIndex = 0, oddIndex = 1;
for(let i = 0; i < n; i++){
if(nums[i] % 2 === 0) {
result[evenIndex] = nums[i];
evenIndex += 2;
} else {
result[oddIndex] = nums[i];
oddIndex += 2;
}
}
return result;
};
//方法三
var sortArrayByParityII = function(nums) {
let oddIndex = 1;
for(let i = 0; i < nums.length; i += 2){
if(nums[i] % 2 === 1){ // 在偶数位遇到了奇数
while(nums[oddIndex] % 2 !== 0) oddIndex += 2;// 在奇数位找一个偶数
[nums[oddIndex], nums[i]] = [nums[i], nums[oddIndex]]; // 解构赋值交换
}
}
return nums;
};
```
-----------------------

View File

@ -182,6 +182,34 @@ func isLongPressedName(name string, typed string) bool {
```
JavaScript
```javascript
var isLongPressedName = function(name, typed) {
let i = 0, j = 0;
const m = name.length, n = typed.length;
while(i < m && j < n){
if(name[i] === typed[j]){ // 相同则同时向后匹配
i++; j++;
} else {
if(j === 0) return false; // 如果是第一位就不相同直接返回false
// 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz"
while(j < n - 1 && typed[j] === typed[j-1]) j++;
if(name[i] === typed[j]){ // j跨越重复项之后再次和name[i]匹配,相同则同时向后匹配
i++; j++;
} else {
return false;
}
}
}
// 说明name没有匹配完 例如 name:"pyplrzzzzdsfa" type:"ppyypllr"
if(i < m) return false;
// 说明type没有匹配完 例如 name:"alex" type:"alexxrrrrssda"
while(j < n) {
if(typed[j] === typed[j-1]) j++;
else return false;
}
return true;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)

View File

@ -154,6 +154,16 @@ func validMountainArray(arr []int) bool {
## JavaScript
```js
var validMountainArray = function(arr) {
if(arr.length < 3) return false;// 一定不是山脉数组
let left = 0, right = arr.length - 1;// 双指针
// 注意防止越界
while(left < arr.length && arr[left] < arr[left+1]) left++;
while(right>0 && arr[right-1] > arr[right]) right--;
// 如果left或者right都在起始位置说明不是山峰
if(left === right && left !== 0 && right !== arr.length - 1) return true;
return false;
};
```
-----------------------

View File

@ -118,7 +118,23 @@ class Solution:
Go
JavaScript
``` javascript
var uniqueOccurrences = function(arr) {
const count = new Array(2002).fill(0);// -1000 <= arr[i] <= 1000
for(let i = 0; i < arr.length; i++){
count[arr[i] + 1000]++;// 防止负数作为下标
}
// 标记相同频率是否重复出现
const fre = new Array(1002).fill(false);// 1 <= arr.length <= 1000
for(let i = 0; i <= 2000; i++){
if(count[i] > 0){//有i出现过
if(fre[count[i]] === false) fre[count[i]] = true;//之前未出现过,标记为出现
else return false;//之前就出现了,重复出现
}
}
return true;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -108,6 +108,15 @@ public:
## JavaScript
```js
var balancedStringSplit = function(s) {
let res = 0, total = 0;//res为平衡字符串数量 total为当前"R"字符和"L"字符的数量差
for(let c of s){// 遍历字符串每个字符
//因为开始字符数量差就是0遍历的时候要先改变数量差否则会影响结果数量
total += c === 'R' ? 1:-1;//遇到"R",total++;遇到"L",total--
if(total === 0) res++;//只要"R""L"数量一样就可以算是一个平衡字符串
}
return res;
};
```
-----------------------

View File

@ -166,6 +166,18 @@ class Solution {
## JavaScript
```js
var sortByBits = function(arr) {
const bitCount = n =>{// 计算n的二进制中1的数量
let count = 0;
while(n){
n &= (n - 1);// 清除最低位的1
count++;
}
return count;
}
// 如果有差则按bits数排如果无差则按原值排
return arr.sort((a,b) => bitCount(a) - bitCount(b) || a - b);
};
```
-----------------------

View File

@ -155,7 +155,23 @@ class Solution:
Go
JavaScript
```javascript
var smallerNumbersThanCurrent = function(nums) {
const map = new Map();// 记录数字 nums[i] 有多少个比它小的数字
const res = nums.slice(0);//深拷贝nums
res.sort((a,b) => a - b);
for(let i = 0; i < res.length; i++){
if(!map.has(res[i])){// 遇到了相同的数字,那么不需要更新该 number 的情况
map.set(res[i],i);
}
}
// 此时map里保存的每一个元素数值 对应的 小于这个数值的个数
for(let i = 0; i < nums.length; i++){
res[i] = map.get(nums[i]);
}
return res;
};
```
-----------------------

View File

@ -126,6 +126,29 @@ class Solution:
Go
JavaScript
```javascript
var balanceBST = function(root) {
const res = [];
// 中序遍历转成有序数组
const travesal = cur => {
if(!cur) return;
travesal(cur.left);
res.push(cur.val);
travesal(cur.right);
}
// 有序数组转成平衡二叉树
const getTree = (nums, left, right) => {
if(left > right) return null;
let mid = left + ((right - left) >> 1);
let root = new TreeNode(nums[mid]);// 中心位置作为当前节点的值
root.left = getTree(nums, left, mid - 1);// 递归地将区间[left,mid1] 作为当前节点的左子树
root.right = getTree(nums, mid + 1, right);// 递归地将区间[mid+1,right] 作为当前节点的左子树
return root;
}
travesal(root);
return getTree(res, 0, res.length - 1);
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)