记录刷题日常,如果你有更好的解题思路,可以分享
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
输入: nums = [3,2,4], target = 6
输出: [1,2]
var twoSum = function(nums, target) {
let map = new Map();
for(let i = 0;i<nums.length;i++) {
const n = nums[i];
const n2 = target - n;
if(map.has(n2)) {
return [map.get(n2),i]
} else {
map.set(n,i)
}
}
};
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
图解
输入: l1 = [2,4,3], l2 = [5,6,4]
输出: [7,0,8]
输入: l1 = [0], l2 = [0]
输出: [0]
输入: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出: [8,9,9,9,0,0,0,1]
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
var addTwoNumbers = function(l1, l2) {
let list = new ListNode(0); // 设置一个节点;
let head = list; // 虚拟节点
let carry = 0; // 进位 两数相加大于1时 carry = 1;
while(l1 || l2) {
let l1_val = l1 ? l1.val : 0;
let l2_val = l2 ? l2.val : 0;
let sum = l1_val + l2_val + carry;
carry = sum >= 10 ? 1 :0;
list.next = new ListNode(sum % 10 , null); // 8 + 9 取 7 carry 进1
list = list.next; // 循环指向下一个节点
l1 = l1 ? l1.next : l1; // l1 和l2链表长度不一定相等
l2 = l2 ? l2.next : l2; //指向下一个节点
}
if(carry == 1) {
list.next = new ListNode(1,null);
}
/*
head的第一个节点为0,并指向list的头节点,有人就会疑问链表已经改变,
但是因为链表是单向的,return head.next就是代表没有0这个节点,返回0以后的节点
*/
return head.next
};
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
思路-双指针
建立一个set对象,当满足条件,没有重复时,滑动右指针,当不满足条件时,滑动左指针,当滑动左指针满足条件时,继续滑动右指针
var lengthOfLongestSubstring = function(s) {
let i = 0; // 左指针
let j = 0 ;// 右指针
let length = 0; // 两个指针之间的长度
let maxLength = 0; // 最大长度
let set = new Set();
// 右指针滑倒最后一个字符的下标
while(j < s.length) {
if(!set.has(s[j])) {
set.add(s[j]); //滑动右指针,满足条件,添加
length++;
if(length > maxLength) {
maxLength = length; // 记录maxlength
}
j++;
} else {
// 不满足条件,即set对象里面存在字符,不需要添加
// 说明左指针指向的元素set中存在,即删除左指针指向的元素
while(set.has(s[j])) {
set.delete(s[i])
i++;
length--;
}
// 左指针继续滑动,满足条件,右指针继续滑动
set.add(s[j]);
length++;
j++;
}
}
return maxLength;
};
4. 寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
思路
合并数组,排序,找到中位数
var findMedianSortedArrays = function(nums1, nums2) {
let num;
let list = nums1.concat(nums2); //合并数组
let arr = quickSort(list); // 从小到大排序
let length = arr.length;
if(length === 0) return null;
if(length % 2 === 0) { // 区分奇偶的中位数
let midIndex = length / 2;
num = (arr[midIndex] + arr[midIndex - 1] ) / 2;
} else {
let midIndex = Math.floor(length / 2);
num = arr[midIndex];
}
return num;
};
function quickSort(arr) {
const length = arr.length;
if (length === 0) return [];
let midIndex = Math.floor(length / 2)
let midValue = arr.splice(midIndex, 1)[0]
let left = []
let right = []
// 注意:这里不用直接用 length ,而是用 arr.length 。因为 arr 已经被 splice 给修改了
for (let i = 0; i < arr.length; i++) {
const n = arr[i]
if (n < midValue) {
// 小于 midValue ,则放在 left
left.push(n)
} else {
// 大于 midValue ,则放在 right
right.push(n)
}
}
return quickSort(left).concat(
[midValue],
quickSort(right)
)
}
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
输入: s = "babad"
输出: "bab"
解释: "aba" 同样是符合题意的答案。
输入: s = "cbbd"
输出: "bb"
var longestPalindrome = function (s) {
// 中心扩散法
if (s.length === 0) return '';
let maxLength = 1; // 非空字符最小回文长度为1
let maxIndex = 0; // 最长回文字串的起始下标
for (let i = 0; i < s.length; i++) {
let currenIndex = i; // 当前下标;
let left = i; // 左指针
let right = i; // 右指针
// 假设s = 'abaccabr'; i = 4; s[i] = c; 和 left-1比较是否相等
while (left != 0) {
if (s[left] === s[left - 1]) {
left--;
} else {
break;
}
}
// 假设s = 'abaccabr'; i = 3; s[i] = c; 和 right + 1比较是否相等
while (right != s.length - 1) {
if (s[right] === s[right + 1]) {
right++;
} else {
break;
}
}
// 假设s = 'abachcabr'; i = 4; s[i] = h; left - 1 和 right + 1比较是否相等
while (left != 0 && right != s.length - 1) {
if (s[left - 1] === s[right + 1]) {
left--;
right++;
} else {
break;
}
}
// 计算满足回文子串的长度 如aa aba -> right - left + 1
if (right - left + 1 > maxLength) {
maxLength = right - left + 1; // 更新最长长度;
maxIndex = left; // 起始下标
}
}
return s.substring(maxIndex, maxIndex + maxLength)
};
6. Z 字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
输入: s = "A", numRows = 1
输出: "A"
思路
创建一个数组,数组的下标对应横向的字符,比如numRors = 4时,下标为 0 ,1,2,3, 当从上往下时,index = index + 1 ,从下往上 index = index - 1;
var convert = function(s, numRows) {
if(numRows == 1) return s;
// 创建一个数组
let arr = new Array(numRows).fill(''); // arr = [ '', '', '' ]
let index = 0; // 数组arr的下标
let direction = 1; // 从上往下为 1 从下往上 - 1;
for (let j = 0; j < s.length; j++) {
arr[index] += s[j]; // 将每列的字符拼接
index = index + direction;
if (index == 0) { // 从上往下
direction = 1;
}
if(index == numRows - 1) { // 从下往上
direction = -1;
}
}
return arr.join('')
};
7. 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
输入: x = 123
输出: 321
输入: x = -123
输出: -321
输入: x = 120
输出: 21
输入: x = 0
输出: 0
var reverse = function(x) {
let min = -Math.pow(2,31) // 边界
let max = Math.pow(2,31)
if(x == 0) return 0;
let y = Math.abs(x) // 绝对值
let arr = String(y).split('').reverse();
let res = Number(arr.join('')) //1230 -> 0321 -> 321
let result = x > 0 ? res : `-${res}`;
return (result > max || result < min) ? 0 : result;
};
函数 myAtoi(string s) 的算法如下:
1 读入字符串并丢弃无用的前导空格
2 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
3 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
4 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
5 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
6 返回整数作为最终结果。
理解
1 去掉前后空格; 2和3 以正负数字开头; 4如果没有数字或以数字结尾返回0; 5 超过边界返回边界值。
var myAtoi = function(s) {
let max = Math.pow(2,31) - 1;
let min = -Math.pow(2,31);
//利用正则
let res = s.trim().match(/^[-|+]{0,1}[0-9]+/)
/**
^:匹配字符串开头
[+|-]:代表一个+字符或-字符
?:前面一个字符可有可无
\d:一个数字
+:前面一个字符的一个或多个
\D:一个非数字字符
*:前面一个字符的0个或多个
*/
if(res != null){
if(res[0] > max){
return max
}
if(res[0] < min){
return min
}
return res[0]
}
return 0
};
9. 回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
输入: x = 121
输出: true
输入: x = -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
输入: x = 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
var isPalindrome = function(x) {
if(x < 0 ) return false;
// 将数字反过来,和x相等
let y = String(x).split('').reverse().join('');
if(x == y) {
return true;
} else {
return false;
}
};
10. 正则表达式匹配-暂没解出
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
输入: s = "aa", p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
输入: s = "ab", p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
var isMatch = function(s, p) {
// return new RegExp('^' + p + '$').test(s);
};
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
输入: height = [1,1]
输出: 1
var maxArea = function(height) {
// 理解题意 组成容器的面积是width * height ,其中组成容器的两根柱子有长有短,以短的为主,
// 如图 width = 9 -2 = 7; height取8和7中的7
// 数组为每根柱子的高度 高度对应的是下标
let left = 0;
let right = height.length - 1;
let max = 0;
while(left < right) {
let h = Math.min(height[left],height[right]); // 高度取最小值
let w = right - left;
let waterArea = h * w;
if(waterArea > max) {
max = waterArea;
}
// 当left = 0;right = height.length - 1时候, height[left] < height[right],left++
if(height[left] < height[right]) {
left ++ ;
} else {
right --;
}
}
return max
};
12. 整数转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
输入: num = 3
输出: "III"
输入: num = 9
输出: "IX"
输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
1 <= num <= 3999
var intToRoman = function(num) {
let t = ['','M','MM','MMM']; // 0 1000 2000 3000
let h = ['','C','CC','CCC','CD','D','DC','DCC','DCCC','CM']; // 0 100-900
let tens = ['','X','XX','XXX','XL','L','LX','LXX','LXXX','XC']; //0 10-90
let bits = ['','I','II','III','IV','V','VI','VII','VIII','IX'];// 0-9
return t[Math.floor(num / 1000)] +
h[Math.floor((num % 1000) / 100)] +
tens[Math.floor((num % 100) / 10)] +
bits[Math.floor(num % 10)]
};
13. 罗马数字转整数
输入: s = "III"
输出: 3
输入: s = "IV"
输出: 4
输入: s = "IX"
输出: 9
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
var romanToInt = function(s) {
let map = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
'a': 4,
'b': 9,
'p': 40,
'q': 90,
'x': 400,
'y': 900
}
let obj = {
'IV':'a',
'IX':'b',
'XL':'p',
'XC':'q',
'CD':'x',
'CM':'y'
}
Object.keys(obj).forEach(key => {
s = s.replace(key,obj[key]);
})
let num = 0;
for(let item of s) {
num += map[item]
}
return num;
};
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
输入: strs = ["flower","flow","flight"]
输出: "fl"
输入: strs = ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀
var longestCommonPrefix = function(strs) {
let length = strs.length;
let list = strs[0]; // 公共子串,以第一个元素为例
let res = '';
for(let i = 0; i < list.length; i++) {
res += list[i]
let flag = strs.every(val => val.indexOf(res) === 0)
if(!flag) return list.substr(0, i)
}
return res
};
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
输入: nums = []
输出: []
输入: nums = [0]
输出: []
图解
var threeSum = function(nums) {
let length = nums.length;
if(length < 3) return [];
let arr= []
nums = nums.sort((a,b) => a-b) // 升序
for(let i = 0; i< length - 2; i++) {
let s = nums[i];
if(s > 0) break; // nums为升序数组,如果第一项都大于0,意味都不满足条件
if (i > 0 && nums[i] == nums[i - 1]) { //元素去重 [-4 ,-1,-1,0,1,2] 如果连续的元素相同,直接跳过
continue;
}
let left = i + 1; // 左指针
let right = length - 1; //右指针
while(left < right) {
let sum = nums[i] + nums[left] + nums[right];
// [-4 ,-1,-1,0,1,2,2,3,3,3,3];
if(sum > 0) {
// 即元素3多次重复,没有必要一直相加,去重
while(nums[right -1] === nums[right]) right--
right --;
}else if(sum < 0) {
//即元素-1多次重复,没有必要一直相加,去重
while(nums[left + 1] === nums[left]) left ++
left ++ ;
}else {
arr.push([nums[i],nums[left],nums[right]]);
while(nums[left + 1] === nums[left]) left ++
while(nums[right - 1] === nums[right]) right--
left ++ ;
right --;
}
}
}
return arr
};
16. 最接近的三数之和
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
输入: nums = [-1,2,1,-4], target = 1
输出: 2
解释: 与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
输入: nums = [0,0,0], target = 1
输出: 0
var threeSumClosest = function(nums, target) {
let ans = Infinity; // Infinity(无穷大)在 js 中是一个特殊的数字,它的特性是:它比任何有限的数字都大
let length = nums.length;
nums.sort((a,b)=>a-b);
for(let i = 0;i< length -2; i++) {
let left = i + 1;
let right = length - 1;
while(left < right) {
let sum = nums[i] + nums[left] + nums[right]; // 和
if(sum > target){
right--;
}else if(sum < target){
left++;
}else{
return target
}
if(Math.abs(sum - target) < Math.abs(ans - target)) {
ans = sum // 返回的三叔之和,sum
}
}
}
return ans
};
17. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入: digits = "23"
输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
输入: digits = ""
输出: []
输入: digits = "2"
输出: ["a","b","c"]
var letterCombinations = function(digits) {
let res = []
let obj = {
2: ['a','b','c'],
3: ['d','e','f'],
4: ['g','h','i'],
5: ['j','k','l'],
6: ['m','n','o'],
7: ['p','q','r','s'],
8: ['t','u','v'],
9: ['w','x','y','z'],
}
if(!digits) return [];
function dfs(curStr,i){
// curStr 当前的字符串 如'a','b'等
if(i > digits.length - 1) {
res.push(curStr);
return
}
let strList = obj[digits[i]]; // ['a','b','c']
for(let item of strList) {
dfs(curStr + item , i + 1)
}
}
dfs('' , 0);
return res;
};
var letterCombinations = function(digits) {
const k = digits.length;
const map = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"];
if(!k) return [];
if(k === 1) return map[digits].split("");
const res = [], path = [];
backtracking(digits, k, 0);
return res;
function backtracking(n, k, a) {
if(path.length === k) {
res.push(path.join(""));
return;
}
for(const v of map[n[a]]) {
path.push(v);
backtracking(n, k, a + 1);
path.pop();
}
}
};
18. 四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。
输入: nums = [1,0,-1,0,-2,2], target = 0
输出: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
输入: nums = [2,2,2,2,2], target = 8
输出: [[2,2,2,2]]
var fourSum = function(nums, target) {
let length = nums.length;
let arr = [];
if(length < 4) return [];
nums.sort((a,b) => a- b)
for(let i = 0; i < length - 3;i++) {
if(i > 0 && nums[i] == nums[i -1]) continue; // 去重
for(let j = i + 1; j< length - 2 ; j++) {
if(j > i + 1 && nums[j] == nums[j -1]) continue;
let left = j + 1;
let right = length - 1;
while(left < right) {
let sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum > target) {
while(nums[right - 1] === nums[right]) right--
right --;
} else if(sum < target) {
while(nums[left + 1] === nums[left]) left ++
left ++;
} else {
arr.push([nums[i],nums[j],nums[left],nums[right]])
while(nums[left + 1] === nums[left]) left ++
while(nums[right - 1] === nums[right]) right--
left ++;
right --;
}
}
}
}
return arr
};
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。
图解
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
if(head.next === null) return null; // 只有一个节点时
let newNode = new ListNode(0); // 创建一个0的节点,方便返回结果
newNode.next = head; // 并指向链表的头节点
let pre = newNode; // 三个指针
let target = head;
let next = head;
while(n) {
n --;
next = next.next;
}
while(next) {
pre = pre.next;
target = target.next;
next = next.next;
}
pre.next = target.next;
return newNode.next;
};
根据链表长度以及倒数的n来查找顺数n的位置
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let dummy = new ListNode(0);
dummy.next = head; // 指向链表头节点
let pre = dummy;
let length = 0; // 计算出链表的长度
while(pre && pre.next) {
pre = pre.next;
length ++;
}
let differ = length - n; // 顺数第几个
pre = dummy; // 计算length的时候pre已经是整个链表了;重新指向dummy
let index = 0
while(index < differ) {
index ++
pre = pre.next;
}
pre.next = pre.next ? pre.next.next : null;
return dummy.next;
};
20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。
输入: s = "()"
输出: true
输入: s = "()[]{}"
输出: true
输入: s = "(]"
输出: false
输入: s = "([)]"
输出: false
输入: s = "{[]}"
输出: true
/**
* @param {string} s
* @return {boolean}
*/
var checkValid = function (left, right) {
if (left === '(' && right === ')') return true;
if (left === '{' && right === '}') return true;
if (left === '[' && right === ']') return true;
return false
}
var isValid = function (s) {
const length = s.length;
if (length % 2 === 1) return false; // 括号成对出现,是偶数
let stack = [];
let leftSmbols = '([{';
let rightSmbols = ')]}';
for (let i = 0; i < length; i++) {
const str = s[i];
if (leftSmbols.includes(str)) {
stack.push(str) // 入栈
} else if (rightSmbols.includes(str)) {
let lastStr = stack[stack.length - 1]; // 最后一位来判断 后进先出一一对应
if (checkValid(lastStr, str)) {
stack.pop() // 出栈 后进先出
} else {
return false
}
}
}
return stack.length === 0;
}