mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-24 00:57:05 +08:00
Update
This commit is contained in:
@ -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,"");
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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--;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -101,7 +101,50 @@ 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;
|
||||
};
|
||||
```
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -328,6 +328,81 @@ 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;
|
||||
};
|
||||
```
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -52,7 +52,7 @@
|
||||
|
||||
**如果想到其实最终利润是可以分解的,那么本题就很容易了!**
|
||||
|
||||
如果分解呢?
|
||||
如何分解呢?
|
||||
|
||||
假如第0天买入,第3天卖出,那么利润为:prices[3] - prices[0]。
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -248,6 +248,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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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; // 正常走到链表末尾,表明没有环
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -349,6 +349,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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -127,6 +127,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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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;// 消灭R,R此时为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";
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -513,6 +513,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);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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,mid−1] 作为当前节点的左子树
|
||||
root.right = getTree(nums, mid + 1, right);// 递归地将区间[mid+1,right] 作为当前节点的左子树
|
||||
return root;
|
||||
}
|
||||
travesal(root);
|
||||
return getTree(res, 0, res.length - 1);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user