01.替换空格
1. 题目描述
请实现一个函数,把字符串中的每个空格替换成"%20"。
例如输入“We are happy.”,则输出“We%20are%20happy.”。
2. 解题思路
一种是正则表达式:直接使用正则表达式全局替换,这种方法取巧一些。
另一种是先计算出来替换后的字符串长度,然后逐个填写字符。这种方法的时间复杂度是。
3. 代码
/**
* 用正则表达式替换
* @param {String} str
*/
function repalceEmpty1(str) {
const re = / /g;
return str.replace(re, "%20");
}
/**
* 将空格替换为 %20
* @param {String} arr
*/
function repalceEmpty2(str) {
str = str.split("");
let count = 0,
i = 0,
j = 0;
for (let i = 0; i < str.length; ++i) {
str[i] === " " && ++count;
}
let length = str.length + count * 2; // 新的字符串的长度:%20比空格长度多2
let result = new Array(length);
while (i < result.length) {
if (str[j] === " ") {
result[i++] = "%";
result[i++] = "2";
result[i++] = "0";
j++;
} else {
result[i++] = str[j++];
}
}
return result.join("");
}
/**
* 测试代码
*/
console.log(repalceEmpty1("We are happy"));
console.log(repalceEmpty2("We are happy"));
02.字符串的全排列
1. 题目描述
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a、b、c 所能排列出来的所有字符串 abc、acb、bac、bca、cab 和 cba。
2. 思路分析
递归全排列法:
就是剑指offer上的做法,也比较容易理解,不过挺少人答的也就是
- 把字符串分为两部分:第一部分为第一个字符,第二部分为第一个字符以后的字符串。 1. 然后接下来求后面那部分的全排列。 1. 再将第一个字符与后面的那部分字符逐个交换
回溯法: 也就是利用树去尝试不同的可能性,不断地去字符串数组里面拿一个字符出来拼接字符串,当字符串数组被拿空时,就把结果添加进结果数组里,然后回溯上一层。(通过往数组加回去字符以及拼接的字符串减少一个来回溯。)
3. 代码实现
// 回溯法
function Permutation(str) {
let res = [];
const pStr = '';
if (str.length <= 0) return res;
let arr = str.split(''); // 将字符串转化为字符数组
res = permutate(arr, pStr, res);
return res;
}
function permutate(arr, pStr, res) {
if (arr.length === 0) {
return res.push(pStr);
}
const isRepeated = new Set();
for (let i = 0; i < arr.length; i++) {
if (!isRepeated.has(arr[i])) {
// 避免相同的字符交换
const char = arr.splice(i, 1)[0];
pStr += char;
permutate(arr, pStr, res);
arr.splice(i, 0, char); // 恢复字符串,回溯
pStr = pStr.slice(0, pStr.length - 1); // 回溯
isRepeated.add(char);
}
}
return res;
}
// 递归全排列法
function Permutation2(str) {
let res = [];
if (str.length <= 0) return res;
let arr = str.split(''); // 将字符串转化为字符数组
res = permutate2(arr, 0, res);
res = [...new Set(res)]; // 去重
res.sort(); // 排序
return res;
}
function permutate2(arr, index, res) {
if (arr.length === index) {
return res.push(arr.join(''));
}
for (let i = index; i < arr.length; i++) {
[arr[index], arr[i]] = [arr[i], arr[index]]; // 交换
permutate2(arr, index + 1, res);
[arr[index], arr[i]] = [arr[i], arr[index]]; // 交换
}
return res;
}
/** * 测试代码 */
console.log(Permutation("ab"));
console.log(Permutation2("abc"));
03.翻转单词顺序
1. 题目描述
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student.",则输出"student. a am I"。
2. 思路分析
进行 2 次不同层次的翻转。第一个层次的翻转,是对整体字符串进行翻转。第二个层次的翻转,是对翻转后字符串中的单词进行翻转。
3. 代码实现
注意:因为 js 按位重写字符,所以第一次整体字符串翻转后的每个字符,都放入了数组中。
/**
* @param {String} sentence
*/
function reverseSentence(sentence) {
// 第一次翻转:每个字符
const chars = sentence.split("").reverse();
let result = "",
last = []; // 保存上一个空格到当前空格之间的所有字符
chars.forEach(ch => {
// 遇到空格,说明之前的字符组成了单词
// 进行第二次翻转:单词
if (ch === " ") {
result += last.reverse().join("");
last.length = 0; // 清空上一个单词
}
last.push(ch);
});
result += last.reverse().join("");
return result;
}
/**
* 测试代码,输出:
* student.a am I
*/
console.log(reverseSentence("I am a student."));
04. 实现atoi
1. 题目描述
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31, 2^31 − 1]。如果数值超过这个范围,qing 返回 INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。
2. 思路分析
这种题目主要就是考察细心,要主动处理所有情况。所以一步步来即可:
- 找出第一个非空字符,判断是不是符号或者数字
- 如果是符号,那么判断正负号
- 如果符号后面跟的不是数字,那么就是非法的,返回 0
- 确定连续数字字符的起始边界
- 计算数字字符的代表的数字大小,并且判断是否越界
- 返回结果的时候注意符号
3. 代码实现
代码如下:
const MIN_INT_ABS = Math.pow(2, 31);
const MAX_INT = MIN_INT_ABS - 1;
/**
* 判断char是否是符号
* @param {String} char
*/
function isSymbol(char) {
return char === "-" || char === "+";
}
/**
* 判断char是否是数字
* @param {String} char
*/
function isNumber(char) {
return char >= "0" && char <= "9";
}
/**
* 模拟atoi(str)
* @param {String} str
*/
function myAtoi(str) {
const length = str.length;
// 找出第一个非空字符,判断是不是符号或者数字
let firstNotEmptyIndex = 0;
for (
;
firstNotEmptyIndex < length && str[firstNotEmptyIndex] === " ";
++firstNotEmptyIndex
) {}
if (
!isSymbol(str[firstNotEmptyIndex]) &&
!isNumber(str[firstNotEmptyIndex])
) {
return 0;
}
// 如果是符号,那么判断正负号
let positive = true,
firstNumberIndex = firstNotEmptyIndex;
if (isSymbol(str[firstNotEmptyIndex])) {
positive = str[firstNotEmptyIndex] === "+";
firstNumberIndex += 1;
}
// 如果符号后面跟的不是数字,那么就是非法的,返回0
if (!isNumber(str[firstNumberIndex])) {
return 0;
}
// 确定连续数字字符的起始边界
let endNumberIndex = firstNumberIndex;
while (endNumberIndex < length && isNumber(str[endNumberIndex + 1])) {
++endNumberIndex;
}
// 计算数字字符的代表的数字大小
// 并且判断是否越界
let result = 0;
for (let i = firstNumberIndex; i <= endNumberIndex; ++i) {
result = result * 10 + (str[i] - "0");
if (positive && result > MAX_INT) {
return MAX_INT;
}
if (!positive && result > MIN_INT_ABS) {
return -1 * MIN_INT_ABS;
}
}
// 返回的时候注意符号
return positive ? result : -1 * result;
}
/**
* 以下是测试代码
*/
console.log(myAtoi(" +1.123sfsdfsd")); // 1
console.log(myAtoi(" -42")); // -42
console.log(myAtoi("words and 987")); // 0
console.log(myAtoi("-91283472332")); // -2147483648
//第二种方法
function StrToInt(str) {
let res = 0,
flag = 1;
const n = str.length;
if (!n) return 0;
if (str[0] === '-') {
flag = -1;
}
for (let i = str[0] === '+' || str[0] === '-' ? 1 : 0; i < n; i++) {
if (!(str[i] >= '0' && str[i] <= '9')) return 0;
res = (res << 1) + (res << 3) + (str[i] - '0');
}
return res * flag;
}