简单篇
7.整数反转
思路
- 反转:使用JS数组的reverse方法 or 手动倒序取值
- 区间:使用Match.pow函数得到n次方值
- 负数:反转过程中用绝对值计算,最后判断原数值是否小于0手动补负号
代码实现
function reverse(x: number): number {
// Math.abs(x) 取绝对值
// -> toString().split('') 转字符串后再转数组
// -> reverse().join() 反转后再拼接
const temp = Math.abs(x).toString().split('').reverse().join('')
// 字符串转数值型
let result = Number(temp)
// 手动补负号
if (x < 0) {
result = - result
}
// 两个区间
const min = - Math.pow(2, 31)
const max = Math.pow(2, 31) - 1
// 按题意判断
return min <= result && result <= max ? result : 0
};
9.回文数
思路
- 将数值转换为字符串
- 找到中间点midIndex将字符串分割为长度等分的两组
- midIndex确定:
midIndex = len / 2
因为向下取整的关系,对于奇数个是准确找到中间值下标的,对于偶数个就需要-1分割
- 奇数个: 5 / 2 = 2 => [1,2,3,4,5] 中间点划下标 2 => 中间值 3
以中间值左右两侧划分[0,midIndex-1],中间值,[midIndex+1,length-1]
代码实现则是
slice(0, midIndex)
,slice(midIndex+1, length)
,slice 前闭后开 - 偶数个:4 / 2 = 2 => [1,2,3,4] 中间点划分下标 [2-1,2] = [midIndex-1,midIndex] => 中间值 2,3
直接中间对半分割[0,midIndex-1],[midIndex, length]
代码实现则是
slice(0, midIndex)
,slice(midIndex, length)
,slice 前闭后开
代码实现
function isPalindrome(x: number): boolean {
const strArr = x.toString().split('')
const numStrLen = strArr.length
if (numStrLen === 1) return true
if (numStrLen === 2) return strArr[0] === strArr[1]
const midIndex = numStrLen / 2
const str1 = strArr.slice(0, midIndex)
const str2 = strArr.slice(numStrLen % 2 ? midIndex + 1 : midIndex, numStrLen)
return str1.join('') === str2.reverse().join('')
};
20.有效的括号
思路
借用栈的特性(先进后出),对目标字符串进行逐字符串拆分,遍历拆分后的字符串组,若是匹配组开头字符则推入栈中,若不是则代表当前字符是结尾字符,即栈中的最后一个字符应该与当前字符为头尾匹配,若匹配成功则栈内最后一个元素出栈,若匹配失败则表明字符串不合法。
代码实现
function isValid(s: string): boolean {
let stack = []
let invalidTag = false
const strArr = s.split('')
const matchCase = {"[": "]", "(": ")", "{": "}"}
// 奇数位一定不能正确匹配
if (strArr.length % 2 > 0) {
return false
}
for(let i in strArr) {
const item = strArr[i]
const itemMtachChar = matchCase[item]
if (itemMtachChar) {
// 是开头字符,直接入栈
stack.push(item)
} else if (matchCase[stack.pop()] !== item) {
// 不是开头字符,判断与栈内最后一个元素是否匹配
invalidTag = true
break
}
}
return invalidTag ? false : stack.length === 0
};
14.最长公共前缀
思路
详细思路见下方代码注释
取第一个元素作为比较的标准对象
(string
类型),遍历传入数组(循环索引i
),标准对象
逐位截取(循环索引j
),传入数组[i][j]
(第i+1个元素的前j+1位字符)和标准对象[j]
(标准对象的前j+1位字符)进行比较,若不满足条件则停止循环,标准对象
的前j-1
位字符便是公共前缀。
注意点:
- 为了减少循环次数,一旦循环过程中有出现过无公共前缀的情况,即可以直接返回结果,不用进行后续无用的比较。
- 将传入数组的第一个元素作为比较标准对象,后续逐位的字符串比较,每次当前传入字符串和标准对象比较完成后,都基于当前得出的公共前缀索引对标准对象字符串进行截取更新。因为比较标准在每一次比较完一个传入字符串时都会更新,那么下一次比较开始前,比较范围已经得到缩小(是基于上一次比较成功的结果上再进行比较)。
代码实现
function longestCommonPrefix(strs: string[]): string {
// 特殊情况处理
if (strs.length <= 1) {
return strs[0] || "";
}
// 公共前缀:取第1个元素作为比较标准
let commPrefix = strs[0];
// 从第2个元素开始遍历比较
for (let i = 1; i < strs.length; i++) {
// 用index作下标标记,逐位截取target进行字符串比较
let index = 0;
// 当前被比较字符串 strs[i] => 字符串的前 index+1 个字符 和 公共前缀的前 index+1 个字符进行比较
for (; index < commPrefix.length && index < strs[i].length; index++) {
// index会一直递增,直到遇到不满足的情况就跳出循环停止递增
if (strs[i][index] !== commPrefix[index]) {
break;
}
}
// 停止循环时的index是不满足条件的index,所以前一位 index-1 才是满足条件的
// substring 刚好是前开后闭,满足我们 index-1 的场景
commPrefix = commPrefix.substring(0, index);
// 初始 commPrefix 是 target,根据每次得出的index进行截取更新,若后一轮得出的index比上一轮大,
// 则截取结果不会发生,便满足公共前缀的需求
// e.g. 1: abc vs. abcd => abc; 2: abc vs. ab => ab; final: abc vs. ab => ab
// 一旦得出过没有匹配的前缀之后,可以直接返回结果,后续不用再比较
if (commPrefix === "") {
break;
}
}
return commPrefix;
}