前言 开始刷力扣,记录一下自己的写法和官方写法,本篇博客将长期置顶,整合我刷的力扣题目,积少成多。
两数之和 题目描述 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); 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]; 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 ; 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; } 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
,
当我们满足二分左侧的A的最右边的值(左侧最大值)小于二分右侧的B的最左边的值(最小值)同时右侧的A的最左边的值(最小值)大于左侧的B的最右边的值(最大值)的时候,我们就可以确定中位数了,如果是奇数,那么就在MaxLeftA和MaxLeftB中,如果是偶数,就需要取这四个值中的中间两个值除以2
当我们的MaxLeftA大于MinRightB的时候,说明我们的i值太大了,需要减小i的值,所以我们的high=i-1,直接舍弃掉数组A二分右半部分的值
当我们的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 var findMedianSortedArrays = function (nums1, nums2 ) { 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 var longestPalindrome = function (s ) { 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 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++ } 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 var convert = function (s, numRows ) { if (numRows === 1 ) { return s; } const rows = new Array (numRows).fill ('' ); for (let i = 0 ; i < s.length ; i++) { const remainder = i % (2 * numRows - 2 ); 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 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 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 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 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 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 var isPalindrome = function (x ) { if (x < 0 || (x % 10 === 0 && x !== 0 )) return false ; 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 ); };
官方题解 官方题解和我第三种方式类似,就不贴了