【力扣刷题】力扣刷题整合
发表于:2024-02-29 |

前言

开始刷力扣,记录一下自己的写法和官方写法,本篇博客将长期置顶,整合我刷的力扣题目,积少成多。

两数之和

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
给定一个整数数组 `nums` 和一个整数目标值 `target`,请你在该数组中找出 和为目标值 `target`  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]


提示:

2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案

我的呆瓜处理

我的思路是,先排序,然后找到比target小的索引,然后俩数之和,如果小于target,小的索引加一,如果大于target,大的索引减一,直到找到俩数之和等于target的索引,然后再找到原数组的索引,返回即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var twoSum = function(nums, target) {
// 从小到大排序
let orginNums=nums.slice();
const sortNums=nums.sort((a,b)=>a-b);
// 找到比target小的索引
const index=sortNums.findLastIndex(num=>num<target);
// 较小元素索引
let smallIndex=0;
// 较大元素索引
let bigIndex=(~index||!index)?index:nums.length-1;
// 俩数之和
while(nums[smallIndex]+nums[bigIndex]!==target){
// 加起来太小了.小的索引加一
if(target>=0){
if((nums[smallIndex]+nums[bigIndex])<target){
smallIndex++;
}
// 加起来太大了,大的索引减一
else{
bigIndex--;
}
}
else{
if((nums[smallIndex]+nums[bigIndex])>target){
bigIndex--;
}
// 加起来太小了,小的索引加一
else{
smallIndex++;
}
}

}
const lastNum=nums[bigIndex];
const firstNum=nums[smallIndex];
// 是否进去过首位
let isHasInterFirst=false
let isHasInterLast=false
for(let i=0;i<orginNums.length;i++){
if(orginNums[i]===firstNum&&!isHasInterFirst){
smallIndex=i;
isHasInterFirst=true;
}
else if(orginNums[i]===lastNum&&!isHasInterLast){
bigIndex=i;
isHasInterLast=true;
}
if(isHasInterFirst&&isHasInterLast){
break;
}
}
return [smallIndex,bigIndex]
};

一波操作下来,能做出来,但是超时了。。。。。。

官方解答学习

方法一:暴力枚举

这个其实我一开始也是这么想的,感觉low了一点就反而想复杂了…

思路及算法

最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。

当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。

1
2
3
4
5
6
7
8
9
var twoSum=function(nums,target){
const n=nums.length;
for(let i=0;j<n;++j){
for(let j=i+1;j<n;++j){
if(nums[i]+nums[j]===target){
return [i,j]
}
}
}

方法二:哈希表

思路及算法

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)O(N)O(N) 降低到 O(1)O(1)O(1)。

这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

1
2
3
4
5
6
7
8
9
10
var twoSum=function(nums,target){
let map=new Map();
for(let i=0;i<nums.length;i++){
let diff=target-nums[i];
if(map.has(diff)){
return [map.get(diff),i]
}
map.set(nums[i],i)
}
}

我在力扣发现了这个写法,和官方给的哈希表算法思路是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const twoSum = (nums, target) => {
const prevNums = {}; // 存储出现过的数字,和对应的索引

for (let i = 0; i < nums.length; i++) { // 遍历元素
const curNum = nums[i]; // 当前元素
const targetNum = target - curNum; // 满足要求的目标元素
const targetNumIndex = prevNums[targetNum]; // 在prevNums中获取目标元素的索引
if (targetNumIndex !== undefined) { // 如果存在,直接返回 [目标元素的索引,当前索引]
return [targetNumIndex, i];
} else { // 如果不存在,说明之前没出现过目标元素
prevNums[curNum] = i; // 存入当前的元素和对应的索引
}
}
}

两数相加

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.


示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]


提示:

每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零

我的呆瓜处理

这里我的处理其实和官方差不多了,这里有个细节,官方要求的是链表格式。所以在我们js中,其实是这样传参的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ListNode(val) {
this.val = val;
this.next = null;
}

let l1 = new ListNode(2);
l1.next = new ListNode(4);
l1.next.next = new ListNode(3);

let l2 = new ListNode(5);
l2.next = new ListNode(6);
l2.next.next = new ListNode(4);

let result = addTwoNumbers(l1, l2);

下面是我的处理方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var addTwoNumbers = function(l1, l2) {
let result = new ListNode(0);
let current = result;
let carry = 0;
while (l1 || l2) {
let sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + carry;
carry = Math.floor(sum / 10);
current.next = new ListNode(sum % 10);
current = current.next;
if (l1) l1 = l1.next;
if (l2) l2 = l2.next;
}
if (carry > 0) {
current.next = new ListNode(carry);
}
return result.next;
}

官方解答学习

思路及算法

由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。

我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。具体而言,如果当前两个链表处相应位置的数字为 n1,n2n1,n2n1,n2,进位值为 carry\textit{carry}carry,则它们的和为 n1+n2+carryn1+n2+\textit{carry}n1+n2+carry;其中,答案链表处相应位置的数字为 (n1+n2+carry) mod 10(n1+n2+\textit{carry}) \bmod 10(n1+n2+carry)mod10,而新的进位值为 ⌊n1+n2+carry10⌋\lfloor\frac{n1+n2+\textit{carry}}{10}\rfloor⌊ 10 n1+n2+carry ​ ⌋

如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 000 。

此外,如果链表遍历结束后,有 carry>0\textit{carry} > 0carry>0,还需要在答案链表的后面附加一个节点,节点的值为 carry\textit{carry}carry。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var addTwoNumbers = function(l1, l2) {
let head = null, tail = null;
let carry = 0;
while (l1 || l2) {
const n1 = l1 ? l1.val : 0;
const n2 = l2 ? l2.val : 0;
const sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = Math.floor(sum / 10);
if (l1) {
l1 = l1.next;
}
if (l2) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
};

无重复字符的最长子串

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。



示例 1:

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。


提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

我的呆瓜处理

我感觉我的处理好像比官方给的还稍微好点,不过底层的逻辑是一样的

我的处理方式是创建一个Map,然后判断循环字符,将这个字符的内容和索引都记录到这个Map当中去,然后这个left是记录,取值最左边的那个参数。然后每次循环都会判断这个字符是否在Map当中,如果在,就将left的值设置为这个字符的索引+1,然后将这个字符的索引和i的差值和max比较,取最大值。最后返回max即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
var lengthOfLongestSubstring = function(s) {
let map = new Map();
let max = 0;
let left = 0;
for(let i=0;i<s.length;i++){
if(map.has(s[i])){
left = Math.max(left,map.get(s[i])+1);
}
map.set(s[i],i);
max = Math.max(max,i-left+1);
}
return max;
};

官方解答学习

方法一:滑动窗口
思路和算法

我们先用一个例子考虑如何在较优的时间复杂度内通过本题。

我们不妨以示例一中的字符串 abcabcbb\texttt{abcabcbb}abcabcbb 为例,找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:

以 (a)bcabcbb\texttt{(a)bcabcbb}(a)bcabcbb 开始的最长字符串为 (abc)abcbb\texttt{(abc)abcbb}(abc)abcbb;
以 a(b)cabcbb\texttt{a(b)cabcbb}a(b)cabcbb 开始的最长字符串为 a(bca)bcbb\texttt{a(bca)bcbb}a(bca)bcbb;
以 ab(c)abcbb\texttt{ab(c)abcbb}ab(c)abcbb 开始的最长字符串为 ab(cab)cbb\texttt{ab(cab)cbb}ab(cab)cbb;
以 abc(a)bcbb\texttt{abc(a)bcbb}abc(a)bcbb 开始的最长字符串为 abc(abc)bb\texttt{abc(abc)bb}abc(abc)bb;
以 abca(b)cbb\texttt{abca(b)cbb}abca(b)cbb 开始的最长字符串为 abca(bc)bb\texttt{abca(bc)bb}abca(bc)bb;
以 abcab(c)bb\texttt{abcab(c)bb}abcab(c)bb 开始的最长字符串为 abcab(cb)b\texttt{abcab(cb)b}abcab(cb)b;
以 abcabc(b)b\texttt{abcabc(b)b}abcabc(b)b 开始的最长字符串为 abcabc(b)b\texttt{abcabc(b)b}abcabc(b)b;
以 abcabcb(b)\texttt{abcabcb(b)}abcabcb(b) 开始的最长字符串为 abcabcb(b)\texttt{abcabcb(b)}abcabcb(b)。
发现了什么?如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!这里的原因在于,假设我们选择字符串中的第 kkk 个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 rkr_kr
k

。那么当我们选择第 k+1k+1k+1 个字符作为起始位置时,首先从 k+1k+1k+1 到 rkr_kr
k

的字符显然是不重复的,并且由于少了原本的第 kkk 个字符,我们可以尝试继续增大 rkr_kr
k

,直到右侧出现了重复字符为止。

这样一来,我们就可以使用「滑动窗口」来解决这个问题了:

我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 rkr_kr
k

在每一步的操作中,我们会将左指针向右移动一格,表示 我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着 以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

在枚举结束后,我们找到的最长的子串的长度即为答案。

判断重复字符

在上面的流程中,我们还需要使用一种数据结构来判断 是否有重复的字符,常用的数据结构为哈希集合(即 C++ 中的 std::unordered_set,Java 中的 HashSet,Python 中的 set, JavaScript 中的 Set)。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。

至此,我们就完美解决了本题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var lengthOfLongestSubstring = function(s) {
// 哈希集合,记录每个字符是否出现过
const occ = new Set();
const n = s.length;
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
let rk = -1, ans = 0;
for (let i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.delete(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
};

寻找两个正序数组的中位数

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。



示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5




提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

我的呆瓜处理

我有点草率了,我就很简单,两个数组合并,然后排序,然后判断奇偶,然后返回中位数,这个时间复杂度是不符合题干要求的

1
2
3
4
5
6
7
8
9
10
var findMedianSortedArrays = function(nums1, nums2) {
let nums=nums1.concat(nums2).sort((a,b)=>a-b);
let len=nums.length;
if(len%2===0){
return (nums[len/2]+nums[len/2-1])/2
}
else{
return nums[Math.floor(len/2)]
}
};

官方解答学习

其实就是二分查找的逻辑,因为这两个数组是有序的,所以我们可以用二分查找的方式来找到中位数,在这里我们将短的数组设为A用来二分,m为A的长度,n为B的长度,A的中间值i为low + Math.floor((high - low) / 2),此时B的长度中间值j就是Math.floor((m + n + 1) / 2) - i

  1. 当我们满足二分左侧的A的最右边的值(左侧最大值)小于二分右侧的B的最左边的值(最小值)同时右侧的A的最左边的值(最小值)大于左侧的B的最右边的值(最大值)的时候,我们就可以确定中位数了,如果是奇数,那么就在MaxLeftA和MaxLeftB中,如果是偶数,就需要取这四个值中的中间两个值除以2
  2. 当我们的MaxLeftA大于MinRightB的时候,说明我们的i值太大了,需要减小i的值,所以我们的high=i-1,直接舍弃掉数组A二分右半部分的值
  3. 当我们的MinRightA小于MaxLeftB的时候,说明我们的i值太小了,需要增大i的值,所以我们的low=i+1,直接舍弃掉数组A二分左半部分的值

我拿了人家的一张图,可以帮助大家更好的理解
解答理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function (nums1, nums2) {
// make sure to do binary search for shorten array
if (nums1.length > nums2.length) {
[nums1, nums2] = [nums2, nums1]
}
const m = nums1.length
const n = nums2.length
let low = 0
let high = m
while (low <= high) {
const i = low + Math.floor((high - low) / 2)
const j = Math.floor((m + n + 1) / 2) - i

const maxLeftA = i === 0 ? -Infinity : nums1[i - 1]
const minRightA = i === m ? Infinity : nums1[i]
const maxLeftB = j === 0 ? -Infinity : nums2[j - 1]
const minRightB = j === n ? Infinity : nums2[j]

if (maxLeftA <= minRightB && minRightA >= maxLeftB) {
return (m + n) % 2 === 1
? Math.max(maxLeftA, maxLeftB)
: (Math.max(maxLeftA, maxLeftB) + Math.min(minRightA, minRightB)) / 2
} else if (maxLeftA > minRightB) {
high = i - 1
} else {
low = i+ 1
}
}
};

最长回文字符串

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。



示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:

输入:s = "cbbd"
输出"bb"


提示:

1 <= s.length <= 1000
s 仅由数字和英文字母组成

我的呆瓜处理

我的想法其实也很简单,就是很简单的循环,一个个循环下去,首位和末位相等的时候,就判断这个字符串是不是回文字符串,然后记录下来,最后返回最长的那个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function (s) {
// 计算s和sReverse的最长公共子串
const len = s.length;
if (len === 1) {
return s
}
let str = s[0]
for (let i = 0; i < len; i++) {
for (let j = len - 1; j > 0; j--) {
if (s[i] === s[j]) {
const curStr = s.substring(i, j + 1);
const reverseStr = curStr.split('').reverse().join('');
if (curStr === reverseStr && curStr.length > str.length) {
str = curStr
}
}
}
}
return str
};

官方处理

他的核心思路就是,找到一个中心点,然后向两边扩散,找到最长的回文字符串,然后记录下来,最后返回最长的那个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function (s) {
if (s.length < 2) {
return s
}
let l = 0;
let r = 0
for (let i = 0; i < s.length; i++) {
// 回文子串长度是奇数
helper(i, i)
// 回文子串长度是偶数
helper(i, i + 1)
}

function helper(m, n) {
while (m >= 0 && n < s.length && s[m] == s[n]) {
m--
n++
}
// 注意此处m,n的值循环完后 是恰好不满足循环条件的时刻 如果此轮询得到回文串长度大于之前记录, 记录此轮循边界
if (n - m - 1 > r - l - 1) {
r = n
l = m
}
}
return s.slice(l + 1, r)
};

Z 字形变换

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Z 字形变换
// 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

// 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

// P A H N
// A P L S I I G
// Y I R
// 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。

// 请你实现这个将字符串进行指定行数变换的函数:

// string convert(string s, int numRows);


// 示例 1:

// 输入:s = "PAYPALISHIRING", numRows = 3
// 输出:"PAHNAPLSIIGYIR"
// 示例 2:
// 输入:s = "PAYPALISHIRING", numRows = 4
// 输出:"PINALSIGYAHRPI"
// 解释:
// P I N
// A L S I G
// Y A H R
// P I
// 示例 3:

// 输入:s = "A", numRows = 1
// 输出:"A"


// 提示:

// 1 <= s.length <= 1000
// s 由英文字母(小写和大写)、',' 和 '.' 组成
// 1 <= numRows <= 1000

我的呆瓜处理

我的想法很简单,就是创建一个数组,然后循环字符串,然后根据规则,将字符串放到数组中去,然后再将数组拼接成字符串返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function (s, numRows) {
// 如果只有一行,直接返回
if (numRows === 1) {
return s;
}
// 初始化一个数组,数组长度为numRows
const rows = new Array(numRows).fill('');
for (let i = 0; i < s.length; i++) {
// 求余数
const remainder = i % (2 * numRows - 2);
// 如果余数小于numRows,直接赋值
if (remainder < numRows) {
rows[remainder] += s[i];
} else {
// 否则,赋值到对应的行
rows[2 * numRows - 2 - remainder] += s[i];
}
}
return rows.join('');
};

官方处理

我看到一个flag的处理方式,感觉挺有意思的,就是用一个flag来控制方向,在转折点改变flag的值,然后根据方向来控制行数的增减,然后将字符串放到数组中去,然后再将数组拼接成字符串返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function (s, numRows) {
if (numRows === 1) return s
let flag = -1, row = 0;
let res = new Array(numRows).fill('')
for (let i = 0; i < s.length; i++) {
res[row] += s[i]
if (row === 0 || row === numRows - 1) flag = -flag
row += flag
}
return res.join('')
};

整数反转

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

// 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0。

// 假设环境不允许存储 64 位整数(有符号或无符号)。


// 示例 1:

// 输入:x = 123
// 输出:321
// 示例 2:

// 输入:x = -123
// 输出:-321
// 示例 3:

// 输入:x = 120
// 输出:21
// 示例 4:

// 输入:x = 0
// 输出:0


// 提示:

// -2^31 <= x <= 2^31 - 1

我的呆瓜处理

我的处理方式是,将数字转成字符串,然后将字符串反转,然后再转成数字,然后判断是否大于2的31次方-1,小于-2的31次方,如果是,就返回0,否则返回这个数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var reverse = function (x) {
let res = 0;
const strX = x.toString();
if (x >= 0) {
res = parseInt(strX.split('').reverse().join(''));
}
else {
res = parseInt('-' + strX.slice(1).split('').reverse().join(''));
}
if (res > Math.pow(2, 31) - 1 || res < Math.pow(-2, 31)) {
return 0;
}
return res;
};

这是我想到的第二种处理方式,就是用取余数的方式,然后将余数进行累加,然后判断是否大于2的31次方-1,小于-2的31次方,如果是,就返回0,否则返回这个数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @param {number} x
* @return {number}
*/
var reverse = function(x) {
let res = 0;
while(x != 0){
res = res * 10 + x % 10;
x = parseInt(x / 10);
}
if(res > Math.pow(2, 31) - 1 || res < Math.pow(-2, 31)){
return 0;
}
return res;
};

官方处理

这里就不贴了,官方给的和我第二种方式类似

字符串转换整数 (atoi)

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

// 函数 myAtoi(string s) 的算法如下:

// 读入字符串并丢弃无用的前导空格
// 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
// 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
// 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
// 如果整数数超过 32 位有符号整数范围 [−2^31, 2^31 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31 − 1 的整数应该被固定为 2^31 − 1 。
// 返回整数作为最终结果。
// 注意:

// 本题中的空白字符只包括空格字符 ' ' 。
// 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。


// 示例 1:

// 输入:s = "42"
// 输出:42
// 解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
// 第 1 步:"42"(当前没有读入字符,因为没有前导空格)
// ^
// 第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
// ^
// 第 3 步:"42"(读入 "42")
// ^
// 解析得到整数 42 。
// 由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。
// 示例 2:

// 输入:s = " -42"
// 输出:-42
// 解释:
// 第 1 步:" -42"(读入前导空格,但忽视掉)
// ^
// 第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数)
// ^
// 第 3 步:" -42"(读入 "42")
// ^
// 解析得到整数 -42 。
// 由于 "-42" 在范围 [-2^31, 2^31 - 1] 内,最终结果为 -42 。
// 示例 3:

// 输入:s = "4193 with words"
// 输出:4193
// 解释:
// 第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
// ^
// 第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
// ^
// 第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
// ^
// 解析得到整数 4193 。
// 由于 "4193" 在范围 [-2^31, 2^31 - 1] 内,最终结果为 4193 。


// 提示:

// 0 <= s.length <= 200
// s 由英文字母(大写和小写)、数字(0-9)、' '、'+'、'-' 和 '.' 组成

我的呆瓜处理

我这里就按照题干的意思来处理,先去掉前导空格,然后判断正负号,然后判断数字,然后返回数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @param {string} s
* @return {number}
*/
var myAtoi = function (s) {
let res = 0;
let sign = 1;
let i = 0;
while (s[i] === ' ') {
i++;
}
if (s[i] === '-' || s[i] === '+') {
sign = s[i] === '-' ? -1 : 1;
i++;
}
while (i < s.length) {
if (s[i] >= '0' && s[i] <= '9') {
res = res * 10 + parseInt(s[i]);
if (sign === 1 && res > Math.pow(2, 31) - 1) {
return Math.pow(2, 31) - 1;
}
if (sign === -1 && res > Math.pow(2, 31)) {
return -Math.pow(2, 31);
}
} else {
break;
}
i++;
}
return res * sign;
};

官方解答

个人认为他的解答甚至不如我这个,就不介绍了

回文数

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

// 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

// 例如,121 是回文,而 123 不是。


// 示例 1:

// 输入:x = 121
// 输出:true
// 示例 2:

// 输入:x = -121
// 输出:false
// 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
// 示例 3:

// 输入:x = 10
// 输出:false
// 解释:从右向左读, 为 01 。因此它不是一个回文数。


// 提示:

// -231 <= x <= 231 - 1


// 进阶:你能不将整数转为字符串来解决这个问题吗?

我的呆瓜处理

如果通过转化成字符串的方式

1
2
3
4
5
6
7
8
9
/**
* @param {number} x
* @return {boolean}
*/
var isPalindrome = function (x) {
if (x < 0) return false;
// 通过字符串的格式
return x.toString() === x.toString().split('').reverse().join('');
};

如果要优化一下时间的可以进行如下处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @param {number} x
* @return {boolean}
*/
var isPalindrome = function (x) {
if (x < 0) return false;
const strX = x.toString();
if (strX.length === 1) return true;
if (x.slice(-1) === '0') {
return false
}
// 通过字符串的格式
let flag = true;
for (let i = 0; i < strX.length / 2 + 1; i++) {
if (strX[i] !== strX[strX.length - 1 - i]) {
flag = false;
break;
}
}
return flag
};

如果用非字符串的方式,我就这样实现了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @param {number} x
* @return {boolean}
*/
var isPalindrome = function (x) {
// 负数和10的倍数都不是回文数
if (x < 0 || (x % 10 === 0 && x !== 0)) return false;
// 小于10的非负数都是回文数
if (x < 10) return true;
let reverse = 0;
while (x > reverse) {
reverse = reverse * 10 + x % 10;
x = parseInt(x / 10);
}
return x === reverse || x === parseInt(reverse / 10);
};

官方题解

官方题解和我第三种方式类似,就不贴了

上一篇:
npm权限错误
下一篇:
如何进行简单的博客搭建