H5W3
当前位置:H5W3 > JavaScript > 正文

【JS】字符串(String)篇——part1

字符串(String)篇——part1

野望发布于 今天 03:42

part1:翻转整数

【JS】字符串(String)篇——part1

心路历程:

1.理解题意:整数、32位、有符号、反转每位数字
2.看懂示例:以上三个示例分别描述了反转、保留符号、去零(如果最高位为0则只保留其他字符)。
3.注意:描述了取值范围,超出范围则返回0

尝试:

1.第一步:判断

2.第二步:翻转
3.第三步:处理

代码如下:

/**
* @description: 翻转整数
* @params {number} str - 传入的整数
* @return {number} 翻转后的整数
*/
function reverse(str) {
let symbol = ""
if (typeof str === "number" && Number.isInteger(str)) {
if (str.toString().length > 32 !== true) {
if (str.toString()[0] === "-") {
symbol = str.toString()[0]
}
// 翻转和处理
let numArr, newStr
if (symbol) {
numArr = [].slice.call(str.toString().slice(1)).reverse()
} else {
numArr = [].slice.call(str.toString()).reverse()
}
newStr = numArr.join("")
for (let i = 0; i < newStr.length; i++) {
if (newStr[i] === "0") {
newStr = newStr.slice(i + 1)
}
i++
}
return parseInt(symbol + newStr)
} else {
return new Error("长度超出限制")
}
} else {
return new Error("非整数")
}
}
console.log(reverse(123)) // 321
console.log(reverse(-123)) // -321
console.log(reverse(120)) // 21

可以看到,按照我分析的思路,以及我现在能够熟练使用的一些方法,已然是自以为是的将它实现了。接下来看看最终答案是什么吧。

终极解决方案————精华来袭:

【JS】字符串(String)篇——part1
代码如下:

/**
* @param {number} x
* @return {number}
*/
const reverse = (x) => {
// 非空判断
if (typeof x !== 'number') {
return;
}
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
// 识别数字剩余部分并翻转
const rest =
x > 0
? String(x)
.split('')
.reverse()
.join('')
: String(x)
.slice(1)
.split('')
.reverse()
.join('');
// 转换为正常值,区分正负数
const result = x > 0 ? parseInt(rest, 10) : 0 - parseInt(rest, 10);
// 边界情况
if (result >= MIN && result <= MAX) {
return result;
}
return 0;
};

分析:
1.思路上:与我的思路基本一致
2.代码上:比我的简洁无数倍,使用的方法也更加准确
3.存在的问题:(1)我的代码首先做了判断整数的处理,但没有判断是否为空的情况;(2)极值的判别片面又草率,题中给出的是数值的取值范围,而我的判断是字符长度,也是明显的答非所问。

代码拆解————深入分析:

1.判别类型及是否为空:

const reverse = (x) => {
// 非空判断
if (typeof x !== 'number') {
console.log(x)
return;
}
}

试问有没有和我一样在判别类型和判别是否为空时使用了两个条件,如:

if (!x) {
return
}
if (typeof x !== "number") {
return
}
// 或
if (!x && typeof x !== "number") {
return
}

在以前我确实没有深入的考虑到typeof x !== “number”这个条件能够进行判空。

现在仔细思考如果传入的值为空的话其实它是undefined,如果是””、null、[]、{}的话,类型又不是number。

所以这样的判空处理确实很聪明。在这种场景下既能确定类型又能确定参数是否为空。

2.极值:

const MAX = 2147483647;
const MIN = -2147483648;

最上面我的代码,在确定边界时简单的判断了参数的长度。很明显是不符合条件的,相信很少有人会犯和我同样的错误。

但是贴这段代码的目的是,我在定义常量的时候总是 const min或const max。这样写当然也可以,但是不够显而易见。通常都是使用全大写的方式。

3.识别数字剩余部分并翻转:

const rest =
x > 0
? String(x)
.split('')
.reverse()
.join('')
: String(x)
.slice(1)
.split('')
.reverse()
.join('');

这段代码太值得学习了,首先是书写风格。我在写三目运算的时候都是一行,很多时候都长到没法看。

其次是使用的方法,准确,准确,准确,我只能这样来形容了。

第一是String(x),使用了一个包装类(其实就是调用了toString()方法,但包装类更简洁清晰)。

第二是split(),这个方法用于字符串分割,我通常是用于处理时间格式等;但精髓在于如果传入的是””,那么它会把字符串的每个字符都分割开来,而且这个方法返回的是一个数组!再对比我自己的方法,还要先使用[].slice.call(x)把它变成数组。惭愧啊,学艺不精啊。

第三是reverse(),这个没什么好说的,数组上的方法用于翻转数组。

第四是join(),把数组的每个元素按照规则拼接成字符串,也很简单。

第五是slice(),按照索引提取字符串的某一个部分,并返回一个新字符串。

4.转换为正常值,区分正负数:

const result = x > 0 ? parseInt(rest, 10) : 0 -parseInt(rest, 10);

怎么说呢,我只能说不怪别人太聪明,只怪自己太愚蠢。我的代码在处理正负整数的时候,首先保留了”-“负号…那正变负不就是0减整数就完事了吗?对不起 x n,代码没写好,数学也没整明白。

parseInt(rest, 10),这里的10是进制,默认就是10进制。所以写作这样也可以:

const result = x > 0 ? parseInt(rest) : 0 - parseInt(rest);

5.边界情况:

if (result >= MIN && result <= MAX) {
return result;
}
return 0;

这个就没什么好说的了。也可以这么写:

return result >= MIN && result <= MAX ? result : 0

复杂度分析:
【JS】字符串(String)篇——part1

巨人的肩膀上好成功,大树底下好乘凉。复盘这段代码确实给我带来了不少的收获。但别急,还有!

【JS】字符串(String)篇——part1

在看到这个方法名的时候我已经被吓到了,这是个嘛?查理 · 芒格曾大致说过,遇到困难的东西要敢于迈步。确实,方法总比困难多,美妙的方法也总是被少数人掌握。

代码如下:

/**
* @param {number} x
* @return {number}
*/
const reverse = (x) => {
// 获取相应数的绝对值
let int = Math.abs(x);
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
let num = 0;
// 遍历循环生成每一位数字
while (int !== 0) {
// 借鉴欧几里得算法,从 num 的最后一位开始取值拼成新的数
num = (int % 10) + (num * 10);
// 剔除掉被消费的部分
int = Math.floor(int / 10);
}
// 异常值
if (num >= MAX || num <= MIN) {
return 0;
}
if (x < 0) {
return num * -1;
}
return num;
};

分析:
1.思路上:欧几里得求最大公约数,看到这句话起初我把重心全放在欧几里得了…但实际上它就是在求最大公约数,这就简单了。然后是翻转,模10取最低位再乘10取最高位,也好理解。
2.代码上:很明显的看到,区别于第一种方法(主要使用String上的方法),这种方法主要使用的是Math上的方法。

代码拆解————深入分析:

1.获取相应数的绝对值

const reverse = (x) => {
let int = Math.abs(x);
}

利用Math.abs将参数处理成正整数。但是这里有一个问题,还是需要判空判类型的,如果不做处理的话Math.abs(x)会返回0或NaN,而且不会抛错,0就不说了,NaN和任何值进行运算都是NaN,所以判空和判类型的处理是必不可少的。改进如下:

const reverse = (x) => {
if (typeof x !== "number") {
return;
}
let int = Math.abs(x)
}

2.极值:

const MAX = 2147483647;
const MIN = -2147483648;

3.遍历循环生成每一位数字:

let num = 0;
while (int !== 0) {
// 借鉴欧几里得算法,从 num 的最后一位开始取值拼成新的数
num = (int % 10) + (num * 10);
// 剔除掉被消费的部分
int = Math.floor(int / 10);
}

while循环确实用的很少,但是不要慌。我们直观的看到循环的入口是int !== 0,而出口是个啥呢?什么时候结束循环呢?

Math.floor()返回小于或等于一个给定数字的最大整数,也就是说当int被消费的很小的时候,int = 0,而此时就会跳出循环了。

回到代码,我们可以代入一个值,比如123。如图:
【JS】字符串(String)篇——part1

看到这里我发现,似乎跟最大公约数没什么关系,反而跟欧几里得算法有点关系…但实际上也很简单。

就按照上面的代入法来一个深入剖析:首先这种方法最精髓的地方就在于while循环内的两行代码。

num这个变量实际上是用来接收int处理结果的变量,而int这个变量是逐渐被“消费”的变量,就好像把一杯水倒进一个空杯子一样。

而在代码上来说,将参数模10,取到的是参数的最低位;而 num * 10 就相当于把num的位数向前推了1位,然后再加上低位数字。然后就相当于做了一次翻转。

【JS】字符串(String)篇——part1

不得不说这个方法真的很巧妙!!!

4.边界情况:

if (num >= MAX || num <= MIN) {
return 0;
}

5.符号:

if (x < 0) {
return num * -1;
}

复杂度分析:
【JS】字符串(String)篇——part1

javascript算法-数据结构
阅读 53更新于 21 分钟前
本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

为了不加班
一个为了写出漂亮代码而努力的前端人
avatar

野望

一个为了写出漂亮代码而努力的前端人

15 声望
2 粉丝

0 条评论
得票时间

avatar

野望

一个为了写出漂亮代码而努力的前端人

15 声望
2 粉丝

宣传栏

part1:翻转整数

【JS】字符串(String)篇——part1

心路历程:

1.理解题意:整数、32位、有符号、反转每位数字
2.看懂示例:以上三个示例分别描述了反转、保留符号、去零(如果最高位为0则只保留其他字符)。
3.注意:描述了取值范围,超出范围则返回0

尝试:

1.第一步:判断

2.第二步:翻转
3.第三步:处理

代码如下:

/**
* @description: 翻转整数
* @params {number} str - 传入的整数
* @return {number} 翻转后的整数
*/
function reverse(str) {
let symbol = ""
if (typeof str === "number" && Number.isInteger(str)) {
if (str.toString().length > 32 !== true) {
if (str.toString()[0] === "-") {
symbol = str.toString()[0]
}
// 翻转和处理
let numArr, newStr
if (symbol) {
numArr = [].slice.call(str.toString().slice(1)).reverse()
} else {
numArr = [].slice.call(str.toString()).reverse()
}
newStr = numArr.join("")
for (let i = 0; i < newStr.length; i++) {
if (newStr[i] === "0") {
newStr = newStr.slice(i + 1)
}
i++
}
return parseInt(symbol + newStr)
} else {
return new Error("长度超出限制")
}
} else {
return new Error("非整数")
}
}
console.log(reverse(123)) // 321
console.log(reverse(-123)) // -321
console.log(reverse(120)) // 21

可以看到,按照我分析的思路,以及我现在能够熟练使用的一些方法,已然是自以为是的将它实现了。接下来看看最终答案是什么吧。

终极解决方案————精华来袭:

【JS】字符串(String)篇——part1
代码如下:

/**
* @param {number} x
* @return {number}
*/
const reverse = (x) => {
// 非空判断
if (typeof x !== 'number') {
return;
}
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
// 识别数字剩余部分并翻转
const rest =
x > 0
? String(x)
.split('')
.reverse()
.join('')
: String(x)
.slice(1)
.split('')
.reverse()
.join('');
// 转换为正常值,区分正负数
const result = x > 0 ? parseInt(rest, 10) : 0 - parseInt(rest, 10);
// 边界情况
if (result >= MIN && result <= MAX) {
return result;
}
return 0;
};

分析:
1.思路上:与我的思路基本一致
2.代码上:比我的简洁无数倍,使用的方法也更加准确
3.存在的问题:(1)我的代码首先做了判断整数的处理,但没有判断是否为空的情况;(2)极值的判别片面又草率,题中给出的是数值的取值范围,而我的判断是字符长度,也是明显的答非所问。

代码拆解————深入分析:

1.判别类型及是否为空:

const reverse = (x) => {
// 非空判断
if (typeof x !== 'number') {
console.log(x)
return;
}
}

试问有没有和我一样在判别类型和判别是否为空时使用了两个条件,如:

if (!x) {
return
}
if (typeof x !== "number") {
return
}
// 或
if (!x && typeof x !== "number") {
return
}

在以前我确实没有深入的考虑到typeof x !== “number”这个条件能够进行判空。

现在仔细思考如果传入的值为空的话其实它是undefined,如果是””、null、[]、{}的话,类型又不是number。

所以这样的判空处理确实很聪明。在这种场景下既能确定类型又能确定参数是否为空。

2.极值:

const MAX = 2147483647;
const MIN = -2147483648;

最上面我的代码,在确定边界时简单的判断了参数的长度。很明显是不符合条件的,相信很少有人会犯和我同样的错误。

但是贴这段代码的目的是,我在定义常量的时候总是 const min或const max。这样写当然也可以,但是不够显而易见。通常都是使用全大写的方式。

3.识别数字剩余部分并翻转:

const rest =
x > 0
? String(x)
.split('')
.reverse()
.join('')
: String(x)
.slice(1)
.split('')
.reverse()
.join('');

这段代码太值得学习了,首先是书写风格。我在写三目运算的时候都是一行,很多时候都长到没法看。

其次是使用的方法,准确,准确,准确,我只能这样来形容了。

第一是String(x),使用了一个包装类(其实就是调用了toString()方法,但包装类更简洁清晰)。

第二是split(),这个方法用于字符串分割,我通常是用于处理时间格式等;但精髓在于如果传入的是””,那么它会把字符串的每个字符都分割开来,而且这个方法返回的是一个数组!再对比我自己的方法,还要先使用[].slice.call(x)把它变成数组。惭愧啊,学艺不精啊。

第三是reverse(),这个没什么好说的,数组上的方法用于翻转数组。

第四是join(),把数组的每个元素按照规则拼接成字符串,也很简单。

第五是slice(),按照索引提取字符串的某一个部分,并返回一个新字符串。

4.转换为正常值,区分正负数:

const result = x > 0 ? parseInt(rest, 10) : 0 -parseInt(rest, 10);

怎么说呢,我只能说不怪别人太聪明,只怪自己太愚蠢。我的代码在处理正负整数的时候,首先保留了”-“负号…那正变负不就是0减整数就完事了吗?对不起 x n,代码没写好,数学也没整明白。

parseInt(rest, 10),这里的10是进制,默认就是10进制。所以写作这样也可以:

const result = x > 0 ? parseInt(rest) : 0 - parseInt(rest);

5.边界情况:

if (result >= MIN && result <= MAX) {
return result;
}
return 0;

这个就没什么好说的了。也可以这么写:

return result >= MIN && result <= MAX ? result : 0

复杂度分析:
【JS】字符串(String)篇——part1

巨人的肩膀上好成功,大树底下好乘凉。复盘这段代码确实给我带来了不少的收获。但别急,还有!

【JS】字符串(String)篇——part1

在看到这个方法名的时候我已经被吓到了,这是个嘛?查理 · 芒格曾大致说过,遇到困难的东西要敢于迈步。确实,方法总比困难多,美妙的方法也总是被少数人掌握。

代码如下:

/**
* @param {number} x
* @return {number}
*/
const reverse = (x) => {
// 获取相应数的绝对值
let int = Math.abs(x);
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
let num = 0;
// 遍历循环生成每一位数字
while (int !== 0) {
// 借鉴欧几里得算法,从 num 的最后一位开始取值拼成新的数
num = (int % 10) + (num * 10);
// 剔除掉被消费的部分
int = Math.floor(int / 10);
}
// 异常值
if (num >= MAX || num <= MIN) {
return 0;
}
if (x < 0) {
return num * -1;
}
return num;
};

分析:
1.思路上:欧几里得求最大公约数,看到这句话起初我把重心全放在欧几里得了…但实际上它就是在求最大公约数,这就简单了。然后是翻转,模10取最低位再乘10取最高位,也好理解。
2.代码上:很明显的看到,区别于第一种方法(主要使用String上的方法),这种方法主要使用的是Math上的方法。

代码拆解————深入分析:

1.获取相应数的绝对值

const reverse = (x) => {
let int = Math.abs(x);
}

利用Math.abs将参数处理成正整数。但是这里有一个问题,还是需要判空判类型的,如果不做处理的话Math.abs(x)会返回0或NaN,而且不会抛错,0就不说了,NaN和任何值进行运算都是NaN,所以判空和判类型的处理是必不可少的。改进如下:

const reverse = (x) => {
if (typeof x !== "number") {
return;
}
let int = Math.abs(x)
}

2.极值:

const MAX = 2147483647;
const MIN = -2147483648;

3.遍历循环生成每一位数字:

let num = 0;
while (int !== 0) {
// 借鉴欧几里得算法,从 num 的最后一位开始取值拼成新的数
num = (int % 10) + (num * 10);
// 剔除掉被消费的部分
int = Math.floor(int / 10);
}

while循环确实用的很少,但是不要慌。我们直观的看到循环的入口是int !== 0,而出口是个啥呢?什么时候结束循环呢?

Math.floor()返回小于或等于一个给定数字的最大整数,也就是说当int被消费的很小的时候,int = 0,而此时就会跳出循环了。

回到代码,我们可以代入一个值,比如123。如图:
【JS】字符串(String)篇——part1

看到这里我发现,似乎跟最大公约数没什么关系,反而跟欧几里得算法有点关系…但实际上也很简单。

就按照上面的代入法来一个深入剖析:首先这种方法最精髓的地方就在于while循环内的两行代码。

num这个变量实际上是用来接收int处理结果的变量,而int这个变量是逐渐被“消费”的变量,就好像把一杯水倒进一个空杯子一样。

而在代码上来说,将参数模10,取到的是参数的最低位;而 num * 10 就相当于把num的位数向前推了1位,然后再加上低位数字。然后就相当于做了一次翻转。

【JS】字符串(String)篇——part1

不得不说这个方法真的很巧妙!!!

4.边界情况:

if (num >= MAX || num <= MIN) {
return 0;
}

5.符号:

if (x < 0) {
return num * -1;
}

复杂度分析:
【JS】字符串(String)篇——part1

本文地址:H5W3 » 【JS】字符串(String)篇——part1

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址