全部代码地址:https://github.com/jimoruyan/leetcode
题库来源:https://leetcode-cn.com/problemset/all/
1.两数之和
// 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
// 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
// 示例:
// 给定 nums = [2, 7, 11, 15], target = 9
// 因为 nums[0] + nums[1] = 2 + 7 = 9
// 所以返回[0, 1]
var twoSum1 = function(nums , target){
for(var i = 0,length = nums.length;i
2.无重复字符的最长子串
// 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
// 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
// 示例:
// 给定 nums = [2, 7, 11, 15], target = 9
// 因为 nums[0] + nums[1] = 2 + 7 = 9
// 所以返回[0, 1]
var twoSum1 = function(nums , target){
for(var i = 0,length = nums.length;i
3.整数反转
// 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
// 示例 1:
// 输入: 123
// 输出: 321
// 示例 2:
// 输入: -123
// 输出: -321
// 示例 3:
// 输入: 120
// 输出: 21
// 注意:
// 假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为[−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
//解题思路
// result * 10 + x % 10 取出末位 x % 10(负数结果还是负数,无需关心正负),拼接到 result 中。
// x / 10 去除末位,| 0 强制转换为32位有符号整数。
// 通过 | 0 取整,无论正负,只移除小数点部分(正数向下取整,负数向上取整)。
// result | 0 超过32位的整数转换结果不等于自身,可用作溢出判断。
var reverse = function (x) {
let res = 0;
while (x !== 0) {
res = res * 10 + x % 10;
x = x / 10 | 0;
}
return (res | 0) === res ? res : 0
};
4.罗马数字转整数
//罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
// 字符 数值
// I 1
// V 5
// X 10
// L 50
// C 100
// D 500
// M 1000
// 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
// 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
// I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
// X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
// C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
// 给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
// 示例 1:
// 输入: 3
// 输出: "III"
// 示例 2:
// 输入: 4
// 输出: "IV"
// 示例 3:
// 输入: 9
// 输出: "IX"
// 示例 4:
// 输入: 58
// 输出: "LVIII"
// 解释: L = 50, V = 5, III = 3.
// 示例 5:
// 输入: 1994
// 输出: "MCMXCIV"
// 解释: M = 1000, CM = 900, XC = 90, IV = 4.
// 思路
// 首先将所有的组合可能性列出并添加到哈希表中
// 然后对字符串进行遍历,由于组合只有两种,一种是 1 个字符,一种是 2 个字符,其中 2 个字符优先于 1 个字符
// 先判断两个字符的组合在哈希表中是否存在,存在则将值取出加到结果 ans 中,并向后移2个字符。不存在则将判断当前 1 个字符是否存在,存在则将值取出加到结果 ans 中,并向后移 1 个字符
// 遍历结束返回结果 ans
var romanToInt = function (s) {
const map = {
I: 1,
IV: 4,
V: 5,
IX: 9,
X: 10,
XL: 40,
L: 50,
XC: 90,
C: 100,
CD: 400,
D: 500,
CM: 900,
M: 1000
};
let ans = 0;
for (let i = 0; i < s.length;) {
if (i + 1 < s.length && map[s.substring(i, i + 2)]) {
ans += map[s.substring(i, i + 2)];
i += 2;
} else {
ans += map[s.substring(i, i + 1)];
i++;
}
}
return ans;
};
//下面有一个更简单的
//主要思路是如果相邻的两个数字左边的比右边的小就减去小值,反之则加之
var romanToInt = function (s) {
var hashNum = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000
}
var result = 0;
for (let i = 0; i < s.length; i++) {
//越界返回und,判断后返回false,三目运算走 result += hashNum[s[i]]
hashNum[s[i]] < hashNum[s[i + 1]] ? result -= hashNum[s[i]] : result += hashNum[s[i]]
}
return result;
};
5.有效的括号
// 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
// 有效字符串需满足:
// 左括号必须用相同类型的右括号闭合。
// 左括号必须以正确的顺序闭合。
// 注意空字符串可被认为是有效字符串。
// 找到最内层的括号对,消去,重复此过程,若存在无法消去的字符则说明字符串无效。
var isValid = function (s) {
while (s.length) {
var temp = s;
s = s.replace('()', '');
s = s.replace('[]', '');
s = s.replace('{}', '');
if (s == temp) return false
}
return true;
};
// 可以边遍历边匹配。也就是遍历的时候遇到左括号存入数组,下次遇到的第一个右括号必须和数组中最后一个元素匹配,否则为无效字符串,匹配完成后从数组中删除此元素。若最终数组为空,表示括号已全部匹配完,字符串有效。
var isValid = function (s) {
var map = {
"(": ")",
"[": "]",
"{": "}"
}
var leftArr = []
for (var ch of s) {
if (ch in map) leftArr.push(ch); //为左括号时,顺序保存
else { //为右括号时,与数组末位匹配
if (ch != map[leftArr.pop()]) return false;
}
}
return !leftArr.length //防止全部为左括号
};
6.移除元素
// 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
// 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
// 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
// 方法1 直接覆盖
// 遇到不同于 val 的项,就将它直接覆盖到 nums 数组中,从第一项开始覆盖
// 遍历完数组,不同于 val 的项都安排到了 nums 数组的前头
var removeElement = (nums, val) => {
let index = 0
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== val) {
nums[index] = nums[i]
index++
}
}
return index
}
// 方法2 末尾项覆盖(双指针)
// 指向头尾的双指针
// 遇到等于val的项,就拿数组的末尾项覆盖它
// 末尾项搬到前面来了,将尾指针左移一位
// 如果遇到不同于val的项,左指针就+1,考察下一项
// 循环结束的条件是两个指针交叉相遇
var removeElement = (nums, val) => {
let index = 0, last = nums.length - 1
while (index <= last) {
if (nums[index] === val) {
nums[index] = nums[last]
last--
} else {
index++
}
}
return index
}
7.实现strStr
// 实现 strStr() 函数。
// 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
var strStr = function (haystack, needle) {
return haystack.indexOf(needle)
};
var strStr = function (haystack, needle) {
if (needle === "") return 0
for (var i = 0; i < haystack.length; i++) {
if (haystack[i] === needle[0]) {
if (haystack.substring(i, i + needle.length) === needle) return i;
}
}
return -1
};
8.搜索查找位置
// 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
// 你可以假设数组中无重复元素。
// 示例 1:
// 输入: [1, 3, 5, 6], 5
// 输出: 2
// 示例 2:
// 输入: [1, 3, 5, 6], 2
// 输出: 1
// 示例 3:
// 输入: [1, 3, 5, 6], 7
// 输出: 4
// 示例 4:
// 输入: [1, 3, 5, 6], 0
// 输出: 0
// 1.暴力解法
var searchInsert = function (nums, target) {
if (nums[0] > target) {
return 0;
}
for (let i = 0; i < nums.length; i++) {
if (nums[i] >= target) {
return i;
}
}
return nums.length - 1
};
// 2.二分法
var searchInsert = function (nums, target) {
if (nums[0] > target) {
return 0;
} else if (nums[nums.length - 1] < target) {
return nums.length;
}
let start = 0;
end = nums.length - 1;
while (start <= end) {
let mid = Math.round((start + end) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return start;
};
9.寻找两个正序数组的中位数
// 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
// 请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)) 。
// 你可以假设 nums1 和 nums2 不会同时为空。
// 示例 1:
// nums1 = [1, 3]
// nums2 = [2]
// 则中位数是 2.0
// 示例 2:
// nums1 = [1, 2]
// nums2 = [3, 4]
// 则中位数是 (2 + 3)/2 = 2.5
var findMedianSortedArrays = function (nums1, nums2) {
const arr = [...nums1, ...nums2].sort((a, b) => a - b);
let { length } = arr;
return length % 2 ? arr[Math.floor(length / 2)] : (arr[length / 2] + arr[length / 2 - 1]) / 2;
}
10.回文数
// 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
// 示例 1:
// 输入: 121
// 输出: true
// 示例 2:
// 输入: -121
// 输出: false
// 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
// 示例 3:
// 输入: 10
// 输出: false
// 解释: 从右向左读, 为 01 。因此它不是一个回文数。
var isPalindrome = function (x) {
var len = String(x).length
var arr = Array.from(String(x))
if (x < 0) {
return false
}
if (len <= 1) {
return true
}
for (let i = 0; i < len;) {
len = arr.length
if (arr.length <= 1) return true
if (Number(arr[i]) == Number(arr[len - 1])) {
arr.splice(0, 1) //删除首尾
arr.splice(len - 2, 1) //删除首尾
} else {
return false
}
}
return true
};
var isPalindrome = function (x) {
return x.toString() == x.toString().split("").reverse().join("");
}
11.最长的公共前缀
// 编写一个函数来查找字符串数组中的最长公共前缀。
// 如果不存在公共前缀,返回空字符串 ""。
// 示例 1:
// 输入: ["flower","flow","flight"]
// 输出: "fl"
// 示例 2:
// 输入: ["dog","racecar","car"]
// 输出: ""
// 解释: 输入不存在公共前缀。
// 说明:
// 所有输入只包含小写字母 a-z 。
// 标签:链表
// 当字符串数组长度为 0 时则公共前缀为空,直接返回
// 令最长公共前缀 ans 的值为第一个字符串,进行初始化
// 遍历后面的字符串,依次将其与 ans 进行比较,两两找出公共前缀,最终结果即为最长公共前缀
// 如果查找过程中出现了 ans 为空的情况,则公共前缀不存在直接返回
// 时间复杂度:O(s)O(s),s 为所有字符串的长度之和
var longestCommonPrefix = function (strs) {
if (strs.length == 0)
return "";
let ans = strs[0];
for (let i = 1; i < strs.length; i++) {
let j = 0;
for (; j < ans.length && j < strs[i].length; j++) {
if (ans[j] != strs[i][j])
break;
}
ans = ans.substr(0, j);
if (ans === "")
return ans;
}
return ans;
};
12.三数之和
// 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
// 注意:答案中不可以包含重复的三元组。
//
// 示例:
// 给定数组 nums = [-1, 0, 1, 2, -1, -4],
// 满足要求的三元组集合为:
// [
// [-1, 0, 1],
// [-1, -1, 2]
// ]
// 标签:数组遍历
// 首先对数组进行排序,排序后固定一个数 nums[i]nums[i],再使用左右指针指向 nums[i]nums[i]后面的两端,数字分别为 nums[L]nums[L] 和 nums[R]nums[R],计算三个数的和 sumsum 判断是否满足为 00,满足则添加进结果集
// 如果 nums[i]nums[i]大于 00,则三数之和必然无法等于 00,结束循环
// 如果 nums[i]nums[i] == nums[i-1]nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
// 当 sumsum == 00 时,nums[L]nums[L] == nums[L+1]nums[L+1] 则会导致结果重复,应该跳过,L++L++
// 当 sumsum == 00 时,nums[R]nums[R] == nums[R-1]nums[R−1] 则会导致结果重复,应该跳过,R--R−−
// 时间复杂度:O(n^2)O(n
// 2
// ),nn 为数组长度
var threeSum = function(nums) {
let ans = [];
const len = nums.length;
if(nums == null || len < 3) return ans;
nums.sort((a, b) => a - b); // 排序
for (let i = 0; i < len ; i++) {
if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
let L = i+1;
let R = len-1;
while(L < R){
const sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
ans.push([nums[i],nums[L],nums[R]]);
while (L 0) R--;
}
}
return ans;
};
13.最接近的三数之和
// 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
// 示例:
// 输入:nums = [-1,2,1,-4], target = 1
// 输出:2
// 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
// 解法:双指针
// 思路
// 确定第一个数,在左右指针移动过程中,更新与target差值最小的结果
// 技巧
// 排序原数组
// nums[right] >= nums[left]
// 确定一个数 x
// res = x + nums[left] + nums[right]
// 当 sum - target < res - target 时
// res = sum
// 当 sum == target 时
// 返回 sum 即为所求
// 当 sum > target
// 根据从小到大的排序方式,左右指针不能再增大,只有右指针能够缩小,进而缩小 sum 值
// right--
// 当 sum < target
// 原理同上,只不过先从小的元素累加起
// left++
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function (nums, target) {
nums.sort((a, b) => a - b);
let res = nums[0] + nums[1] + nums[2];
let n = nums.length;
for (let i = 0; i < n; i++) {
let left = i + 1;
let right = n - 1;
while (left < right) {
let sum = nums[i] + nums[left] + nums[right];
if (Math.abs(res - target) > Math.abs(sum - target)) {
res = sum;
} else if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else if (sum === target) {
return res;
}
}
}
return res;
};
14.电话号码的字母组合
//给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
// 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
// 示例:
// 输入:"23"
// 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
var letterCombinations = function (digits) {
let result = []
if (digits.length == 0) return []
let numMap = {
2: 'abc',
3: 'def',
4: 'ghi',
5: 'jkl',
6: 'mno',
7: 'pqrs',
8: 'tuv',
9: 'wxyz'
}
for (let code of digits) {
let word = numMap[code]
if (result.length > 0) {
let tmp = []
for (let char of word) {
for (let old of result) {
tmp.push(old + char)
}
}
result = tmp
} else {
result.push(...word)
}
}
return result
}
15.括号生成
// 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
// 示例:
// 输入:n = 3
// 输出:[
// "((()))",
// "(()())",
// "(())()",
// "()(())",
// "()()()"
// ]
let generateParenthesis = (n) => {
let res = []
let dfs = (s, left, right) => {
if (left == n && right == n) return res.push(s)
if (left < n) dfs(s + '(', left + 1, right)
if (right < left) dfs(s + ')', left, right + 1)
}
dfs('', 0, 0)
return res
}
16.合并k个排序链表
// 合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
// 示例:
// 输入:
// [
// 1->4->5,
// 1->3->4,
// 2->6
// ]
// 输出: 1->1->2->3->4->4->5->6
function ListNode(val) {
this.val = val;
this.next = null;
}
var mergeKLists = function (lists) {
if (!lists || lists.length == 0) return null;
let arr = [];
let res = new ListNode(0);
lists.forEach(list => {
let cur = list;
while (cur) {
arr.push(cur.val);
cur = cur.next;
}
})
let cur = res;
arr.sort((a, b) => a - b).forEach(val => {
let node = new ListNode(val);
cur.next = node;
cur = cur.next;
})
return res.next;
};
17.排序数组去重
// 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
// 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
// 示例 1:
// 给定数组 nums = [1,1,2],
// 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
// 你不需要考虑数组中超出新长度后面的元素。
// 示例 2:
// 给定 nums = [0,0,1,1,1,2,2,3,3,4],
// 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
// 你不需要考虑数组中超出新长度后面的元素。
var removeDuplicates = function (nums) {
var j = 0;
var n = nums.length;
for (let i = 1; i < n; i++) {
if (nums[i] != nums[i - 1]) {
j++;
nums[j] = nums[i];
}
}
return j + 1;
};