回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
难易度:容易
解题思路
- 转换为字符串操作
var isPalindrome = function (x: number): boolean {
if (x === 0) return true
if (!x || x < 0) return false
let rev = +(x + '').split('').reverse().join('')
return x == rev
};
-
进阶,不操作字符串,反转一半数字
按照题目,回文数指:如果数字的位数是奇数,那么去除掉中间位数的数字,前一半数字,等于后一半数字的反转;如果数字的位数是偶数,那么前一半数字,等于后一半数字的反转。
所以,只需要反转一半的数字与原数字的前一半数字进行比较即可
1223221反转
每一次反转,反转数
rev都加上数x的个位数,x则应该舍掉个位数,直到rev >= x,从下表可看出rev < x代表反转还没有进行到一半次数/值 x rev 1 122322 1 2 12232 12 3 1223 122 4 122 1223
var isPalindrome2 = function (x: number): boolean {
// x < 0 或者 x的个位数是0都不满足(x % 10)
if (x === 0) return true
if (!x || x < 0 || x % 10 == 0) return false
let rev = 0 // 存储已反转的数
// 结束点是rev < x 只要x > rev 那就说明这个数还没有反转到一半
while (rev < x) {
rev = rev * 10 + x % 10 // 每一次都取x的个位数加在反转结果上
x = Math.floor(x / 10) // 每一次去掉x的个位数
}
// 如果x的位数是基数位,则最终结果 rev位数的肯定比x多一位,
// rev的个位为原x的中间位数字
// rev / 10 取整丢掉个位数再比较
return x == rev || Math.floor(rev / 10) == x
};
Z字形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:
| L | C | I | R | ||||
| E | T | O | E | S | I | I | G |
| E | D | H | N |
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。
难易度:中等
解题思路
从上图可以看出
-
当笔向下画的时候,依次向每一行添加一个字符串,最大行numRows - 1
-
向右画的时候,依次向上一行添加一个元素,直到画到第0行
用一个标识downFlag,来表示是不是向下画,sIdx表示在第几行添加字符串,临界点就是sIdx == 0 || numRows - 1 == sIdx;用一个数组来装所有行,sIdx就为数组的下标
所以数组结构就是这样
[
'', // 第0行
'', // 第1行
'' // 第2行
]
// 向下画 依次向[sIdx]添加一个字符串 sIdx += 1
[
'L',
'E',
'E',
]
// 向右画 此时往[sIdx - 1]添加一个字符串 所以sIdx -= 1
[
'L',
'ET',
'E,'
]
var convert = function (s: string, numRows: number): string {
const len = s.length
if (!s || len <= numRows || numRows == 1) return s
// 构建一个numRows个元素的数组,用来装所有拼接字符串
const strArr = []
for (let i = 0; i < numRows; i++) {
strArr[i] = ''
}
let sIdx = 0 // 需要给strArr的第几个元素加上字符
let downFlag = false // 是否是向下
// 根据规律 向下画时,依次向所有行添加一个字符串,画到第numRows - 1行 往右画
// 向右画时,依次向上一行画一个元素,划到第0行又往下画
for (let i = 0; i < len; i++) {
strArr[sIdx] += s[i]
if (sIdx == 0 || sIdx == numRows - 1) downFlag = !downFlag
sIdx = downFlag ? sIdx + 1 : sIdx - 1
}
// 拼接结果字符串
let res = ''
strArr.forEach(it => res += it)
return res
};
最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""
·示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
·示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
所有输入只包含小写字母 a-z 。
难易度:容易
解题思路:
- 深度遍历
用字符串数组的第0位,与数组之后的所有位进行比较。
-
第一遍循环,将
strs[0]和strs[1]进行比较,找出这两个字符串的最长公共前缀- 假设
strs[0]是strs[1]的子串 - 如果
strs[1].indexOf(strs[0]) == 0,说明str[1]和strs[0]的最长公共前缀就是strs[0] - 利用这个最长公共前缀,与
strs[2]继续比较,找出最长公共前缀 - 如果
strs[1].indexOf(strs[0]) != 0,就删除str[0]的最后一个字符,再继续比较
- 假设
-
上述情况的结束点就是
str[0]经过截取后只剩空字符串,说明他们没有公共前缀;strs数组循环完毕,那么经过截取后的str[0]就是最长公共前缀
const longestCommonPrefix = function (strs: string[]): string {
const len = strs.length
if (!len) return ''
if(len == 1) return strs[0]
// 找出字符串数组的第一位,用它和后面所有字符串比较
let s = strs[0], index = 1
while(index < len) {
// 找到s 和 后一个字符串的 最长的公共前缀
// 保存这个前缀
// 继续用这个前缀和 再后一个字符串相比,找到最长的公共前缀
// 直到公共前缀为空,或者遍历完整个字符串数组
// 如果s被 strs[index]整个包含,说明s就是这两个字符串的最长公共前缀,直接进入下一次循环
// 如果s已经只剩空字符串,跳出循环
while(strs[index].indexOf(s) && s.length){
// 否则,每次丢弃s最后一个字符,再进行比较
s = s.substring(0, s.length - 1)
}
index++
}
// 最后 s 就最最长公共前缀
return s
};
- 指针
看一遍题目,就应该想到的方法,把所有字符串的每一位字符,从第0位开始直接进行比较。
那么结束点就是遇到了不完全相等的字符,或者遍历完了最短的字符串,所以,这里可以先把最短的字符串找到。
const longestCommonPrefix2 = function (strs: string[]): string {
const len = strs.length
if (!len) return ''
if (len == 1) return strs[0]
let minStr = strs[0]
// 找到最短字符串
for (let i = 1; i < len; i++) {
if (minStr.length > strs[i].length) {
minStr = strs[i]
}
}
let index = 0 // 指针
let temp = '' // 最长公共前缀
// 遍历最短字符串
for (const s of minStr) {
// 遍历数组
for (let i = 0; i < len; i++) {
// 出现不相同的字符
if (strs[i][index] != s) {
return temp
}
}
temp += s
index++
}
return temp
}
整数转罗马数字
罗马数字包含以下七种字符: 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 。
-
特殊情况
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内
-
示例:
输入: 3输出: "III"
难易度:中等
解题思路:
数字的千位数 = 罗马字符的千位数里面寻找千位数值 相加 数字的百位数 = 罗马字符的百位数里面寻找百位数值 相加
从高位到低位相加(从大数字到小数字相加) ...
示例: 3459 = 1000 + 1000 + 1000 + 400 + 50 + 9
=> M + M + M + CD + L + IX = MMMCDLIX
3668 = 1000 + 1000 + 1000 + 500 + 100 + 50 + 10 + 50 + 10 + 10 + 10
=> M + M + M + D + C + L + X + V + I + I + I = MMMDCLXVIII
var intToRoman = function (num: number): string {
if (!num) return ''
// 创建数字和罗马数对应的字典 对象是无序的,所以这里还是用数组来存
const keys: number[] = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
const values: string[] = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
let res = ''
for (let i = 0, len = keys.length; i < len; i++) {
while (num >= keys[i]) {
res += values[i]
num -= keys[i]
}
}
return res
};
最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
难易度:中等
解题思路
- 暴力解法
如果一个字符串倒置,等于原字符串,那么这个字符串就是回文串,所以只需要把给定字符串的,每一个子串进行是不是回文串的判断即可,但是这个方法性能很低,字符串长度达到一定程度,就会超时。
// 检查一个字符串是不是回文串
const reverseStr = function (s: string): boolean {
let sarr = s.split('')
let rs = sarr.reverse().join('')
return rs == s
}
var longestPalindrome = function (s: string): string {
if (!s) return ''
if (s.length == 1 || reverseStr(s)) return s
let temp = ''
let res = ''
let ss = s
// 两层循环,找出所有子串
while (ss.length) {
for (let i of ss) {
temp += i
if (reverseStr(temp)) {
res = res.length > temp.length ? res : temp
}
}
temp = ''
ss = ss.substr(1)
}
return res
};
- 向两边扩散法
找定一个字符串,以这个字符为中心,向两边扩散,如果两边的字符串相等,继续向两边扩散,直到两边不相等,那么中间的字符串就是回文串;
以字符串长度为标准,多次遍历,找出其中最长的字符串,就是答案
var longestPalindrome2 = function (s: string): string {
if (!s) return ''
let len = s.length
if (len == 1) return s
let res = s[0];
for (let i = 0; i < len; i++) {
for (let j = 1; j <= 2; j++) { //偶数奇数回文串
let left = i, right = i + j;
while (left >= 0 && right < len && s[left] === s[right]) {
left--, right++; //向外扩展直到两端不相同
};
let length = right - left - 1; //(right - 1) - (left + 1) + 1
if (length > result.length) {
res = s.substr(left + 1, length);
}
}
}
return res;
}