1. 整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。
解法:判断正负,将绝对值转字符串,再翻转字符串,判断是否超出范围,返回整数
var reverse = function(x) {
let max = Math.pow(2, 31) - 1
let isPositive = x > 0
let str = Math.abs(x) + ''
let reverseStr = +str.split('').reverse().join('')
if ( isPositive ) {
return reverseStr > max ? 0 : reverseStr
} else {
return reverseStr > max + 1 ? 0 : -reverseStr
}
return 0
};
2. 回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数
解法一:将整数转化成字符串,再将字符串翻转,对比前后字符串
var isPalindrome = function(x) {
let str = x + ''
let reverseStr = str.split('').reverse().join('')
return str === reverseStr
};
解法二:将整数转化成字符串,截取后半段翻转与前半段相比
var isPalindrome = function(x) {
let str = x + ''
let len = str.length
let cutLen = Math.ceil(len / 2) // 一半的长度
let leftStr = ''
if (len % 2 > 0) {
leftStr = str.slice(0, cutLen - 1)
} else {
leftStr = str.slice(0, cutLen)
}
let rightStr = str.slice(cutLen).split('').reverse().join('')
return leftStr === rightStr
};
解法三:将整数转化成字符串,遍历字符串,对比正序和倒序相对应的项
var isPalindrome = function(x) {
let str = x + ''
let len = str.length -1
let cutLen = Math.ceil(len / 2) // 一半的长度
for (let i = 0; i < cutLen; i ++) {
if (str[i] !== str[len-i]) {
return false
}
}
return true
};
性能表现 解法三 > 解法一 ~= 解法二
3. 到达终点数字
在一根无限长的数轴上,你站在0的位置。终点在target的位置。 每次你可以选择向左或向右移动。第 n 次移动(从 1 开始),可以走 n 步。返回到达终点需要的最小移动次数。
解法: 假设a为正向移动步数,b为负向移动步数,有a-b==/target/,sum==a+b; 则有sum-/target/ == 2b;
var reachNumber = function(target) {
let sum = 0
let i = 1
let tmp = Math.abs(target)
while (sum < tmp || (sum - tmp) % 2) {
sum += i
++i
}
return i - 1;
};
4.数组去重
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
解法一:双层循环
var removeDuplicates = function(nums) {
for(let i = 0; i < nums.length;i++) {
for(let j = i + 1; j < nums.length;) {
if (nums[i] == nums[j]) {
nums.splice(j, 1)
} else {
j++
}
}
}
return nums.length
}
解法二:单层循环
已知输入是排序后的数组,所以只要考虑前后是否相等
var removeDuplicates = function(nums) {
for(let i = 0; i < nums.length;) {
if (nums[i] == nums[i+1]) {
nums.splice(i+1, 1)
} else {
i++
}
}
return nums.length
}
解法三:ES6 Set
var removeDuplicates = function(nums) {
nums = [...new Set(nums)]
return nums.length
}
5.寻找数组的中心索引
数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
解法:左右两边加中心索引的值为总和
left + num[i] + right = sum
left = right
2 * left + num[i] = sum
var pivotIndex = function(nums) {
if (!nums.length) return -1
let left = 0
let sum = nums.reduce((a, b) => a + b)
for (let i = 0; i < nums.length; i++) {
if (left * 2 + nums[i] === sum ) {
return i
}
left += nums[i]
}
return -1
}
6.至少是其他数字两倍的最大数
在一个给定的数组nums中,总是存在一个最大元素 。 查找数组中的最大元素是否至少是数组中每个其他数字的两倍。 如果是,则返回最大元素的索引,否则返回-1
var dominantIndex = function(nums) {
if (nums.length === 1) return 0
let maxIndex = 0
for (let i = 1, len = nums.length; i < len; i++) {
if (nums[i] > nums[maxIndex]) {
maxIndex = i
}
}
let max = nums.splice(maxIndex, 1)[0]
let flag = false
for (let i = 0, len = nums.length; i < len; i++) {
if (max < nums[i] * 2) {
flag = true
}
}
return flag ? -1 : maxIndex
}
7.加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
输入: [1,2,3] 输出: [1,2,4]
var plusOne = function(digits) {
for (let i = digits.length - 1; i >= 0; i--) {
if (digits[i] === 9) {
digits[i] = 0
if (i == 0) {
digits.splice(0, 0, 1)
return digits
}
} else {
digits[i] = digits[i] + 1
return digits
}
}
}
8.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组。
原生
var reverseString = function(s) {
return s.reverse()
}
解法一:遍历
var reverseString = function(s) {
let len = s.length
for (let i = 0; i < Math.floor(len / 2); i++ ) {
let temp = s[i]
s[i] = s[len - i - 1]
s[len - i - 1] = temp
}
return s
}
解法二:递归
var reverseString = function(s) {
var reverse = function (s, i) {
let len = s.length
if (i >= Math.floor(len / 2)) return s
let temp = s[i]
s[i] = s[len - i - 1]
s[len - i - 1] = temp
return reverse(s, ++i)
}
return reverse(s, 0)
};
9.翻转字符串里的单词
给定一个字符串,逐个翻转字符串中的每个单词,输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括
var reverseWords = function(s) {
let arr = s.split(' ')
let reverseArr = arr.reverse().filter(item => item)
return reverseArr.join(' ')
}
10.求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 [ n/2 ] 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数
解法:先计算到每个值出现的次数,再找出最大次数的那个值
var majorityElement = function(nums) {
let map = {}
for (let i = 0; i < nums.length; i++) {
if (map[nums[i]]) {
map[nums[i]] = (map[nums[i]]) + 1
} else {
map[nums[i]] = 1
}
}
let maxCount = 0
let maxValue
for (prop in map) {
if (map[prop] > maxCount) {
maxCount = map[prop]
maxValue = prop
}
}
return maxValue
}
11.无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
var lengthOfLongestSubstring = function(s) {
let tempArr = []
let maxArr = []
for (let i = 0; i < s.length; i++) {
let index = tempArr.indexOf(s[i])
if (index > -1) {
if (tempArr.length > maxArr.length) {
maxArr = tempArr
}
tempArr = tempArr.slice(index + 1)
}
tempArr.push(s[i])
}
return Math.max(maxArr.length, tempArr.length)
}
12.盛最多水的容器
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
解法一:暴力法,遍历每一个值
var maxArea = function(height) {
let max = 0
let len = height.length
for (let i = 0; i < len -1 ; i++) {
for (let j = i+1; j < len; j++) {
let area = Math.min(height[i], height[j]) * (j - i)
max = Math.max(max, area)
}
}
return max
}
解法二:双指针
var maxArea = function(height) {
let length = height.length
let left = 0
let right = length - 1
let max = 0
while (left < right) {
let area = Math.min(height[left], height[right]) * (right - left)
max = Math.max(max, area)
height[left] > height[right] ? right-- : left++
}
return max
}
13.救生艇
第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
解法:双指针,先给数组排序,每次都让最重的上船
var numRescueBoats = function(people, limit) {
let left = 0
let right = people.length - 1
let total = 0
people.sort((a, b) => a - b)
while (left <= right) {
if (people[left] + people[right] <= limit) {
left++
}
total++
right--
}
return total
};
14.移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
var removeElement = function(nums, val) {
for (let i = 0; i < nums.length;) {
nums[i] === val ? nums.splice(i, 1) : i++
}
return nums.length
};
15.相同的树
给定两个二叉树,编写一个函数来检验它们是否相同,如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同。
思路:先判断节点的值,再判断节点的左右节点
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} p
* @param {TreeNode} q
* @return {boolean}
*/
var isSameTree = function(p, q) {
if ( p == null && q == null){
return true;
}
if (p !== null && q !== null && p.val === q.val){
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
} else {
return false;
}
};
16.Pow(x, n)
实现幂函数,n 范围[-2^31, 2^31-1]
解法一:先判断n的正负, 再使用递归
/**
* @param {number} x
* @param {number} n
* @return {number}
*/
var myPow = function(x, n) {
var pow = function(p, n) {
if (n === 1) return p
return pow(p * x, --n)
}
if (n === 0) {
return 0
} else if ( n > 0) {
return pow(x, n)
} else {
return 1 / pow(x, Math.abs(n))
}
}