收编leetcode,能收多少收多少,目前进度 26/32
一.数组
关键词:哈希表 双箭头 至少一次,出现两次,记出现次数
1.两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
【LeetCode 直通车】:1 两数之和(简单)
var twoSum = function(nums, target) {
map = new Map()
for(let i = 0; i < nums.length; i++) {
x = target - nums[i]
if(map.has(x)) {
return [map.get(x),i]
}
map.set(nums[i],i)
}
};
2.寻找两个正序数组的中位数
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
【LeetCode 直通车】:4 寻找两个正序数组的中位数(困难)
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1,nums2){
let arr = nums1.concat(nums2);
arr.sort((a,b)=>{
return a-b;
});
if(arr.length % 2 == 0){
return (arr[arr.length/2]+arr[arr.length/2-1])/2;
}else {
return arr[Math.floor(arr.length/2)];
}
}
3.整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
【LeetCode 直通车】:7 整数反转(简单)
var reverse = function(x) {
let res = 0;
while(x){
res = res * 10 + x % 10;
if(res > Math.pow(2, 31) - 1 || res < Math.pow(-2, 31)) return 0;
x = ~~(x / 10);
}
return res;
};
4.盛最多水的容器
【LeetCode 直通车】:11 盛最多水的容器(中等)
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let n = height.length;
let left = 0, right = n - 1;
let maxOpacity = 0;
while (left < right) {
let res = Math.min(height[left], height[right]) * (right - left);
maxOpacity = Math.max(maxOpacity, res);
if (height[left] < height[right]) left++
else right--;
}
return maxOpacity;
};
5.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
【LeetCode 直通车】:15 三数之和
const threeSum = (nums) => {
nums.sort((a, b) => a - b); // 排序
const res = [];
for (let i = 0; i < nums.length - 2; i++) { // 外层遍历
let n1 = nums[i];
if (n1 > 0) break; // 如果已经爆0,不用做了,break
if (i - 1 >= 0 && n1 == nums[i - 1]) continue; // 遍历到重复的数,跳过
let left = i + 1; // 左指针
let right = nums.length - 1; // 右指针
while (left < right) {
let n2 = nums[left], n3 = nums[right];
if (n1 + n2 + n3 === 0) { // 三数和=0,加入解集res
res.push([n1, n2, n3]);
while (left < right && nums[left] == n2) left++; // 直到指向不一样的数
while (left < right && nums[right] == n3) right--; // 直到指向不一样的数
} else if (n1 + n2 + n3 < 0) { // 三数和小于0,则左指针右移
left++;
} else { // 三数和大于0,则右指针左移
right--;
}
}
}
return res;
};
6.最接近的三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
【LeetCode 直通车】:16 最接近的三数之和
var threeSumClosest = function(nums, target) {
// 先对数组进行排序, 为了后面双指针计算值做准备
nums = nums.sort((a, b) => a - b);
let min = +Infinity,
result = 0;
for (let i = 0; i < nums.length; i++) {
// 循环内部声明左右指针, 初始化 左指针位于索引下一个, 右指针位于数组最后一位索引
let left = i + 1,
right = nums.length - 1;
// 结束条件为左右指针重合
while (left < right) {
// 每次用3个索引计算合然后更新最小值
const sum = nums[i] + nums[left] + nums[right];
const res = sum - target;
if (Math.abs(res) < min) {
min = Math.abs(res);
result = sum;
}
// 如果合的值大于目标值target, 那么表示需要减少合值再计算, 所以右指针后退一位, 反之左指针前进一位
if (res >= 0) {
right--;
} else {
left++;
}
}
}
return result;
};
7.四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
【LeetCode 直通车】:18 四数之和
var fourSum = function(nums, target) {
const len = nums.length;
if(len < 4) return [];
nums.sort((a, b) => a - b);
const res = [];
for(let i = 0; i < len - 3; i++) {
// 去重i
if(i > 0 && nums[i] === nums[i - 1]) continue;
for(let j = i + 1; j < len - 2; j++) {
// 去重j
if(j > i + 1 && nums[j] === nums[j - 1]) continue;
let l = j + 1, r = len - 1;
while(l < r) {
const sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum < target) { l++; continue}
if(sum > target) { r--; continue}
res.push([nums[i], nums[j], nums[l], nums[r]]);
while(l < r && nums[l] === nums[++l]);
while(l < r && nums[r] === nums[--r]);
}
}
}
return res;
};
8.删除有序数组中的重复项
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
【LeetCode 直通车】:26 删除有序数组中的重复项 (简单)
var removeDuplicates = function(nums) {
const n = nums.length;
if (n === 0) {
return 0;
}
let fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] !== nums[fast - 1]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
};
9.移除元素
给你一个数组 nums
**和一个值 val
,你需要 原地 移除所有数值等于 val
**的元素,并返回移除后数组的新长度。
【LeetCode 直通车】:27 移除元素(简单)
var removeElement = function(nums, val) {
const n = nums.length;
let left = 0;
for (let right = 0; right < n; right++) {
if (nums[right] !== val) {
nums[left] = nums[right];
left++;
}
}
return left;
};
10.下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
【LeetCode 直通车】:31 下一个排列(中等)
// 为了找更大的下一个数字
// 1、需要将左边的小数和右边的大数交换
// 2、然后将交换完的大数后改成升序
// 找小数
let left = 0, temp = 0
for (let i = nums.length - 1; i > 0; i--) {
if (nums[i] > nums[i - 1]) {
left = i - 1
temp = i
break
}
}
// 找大数(因为temp后面都是递减的)
let right = 0
for (let i = nums.length - 1; i >= temp; i--) {
if (nums[i] > nums[left]) {
right = i
break
}
}
// 交换
let a = nums[left]
nums[left] = nums[right]
nums[right] = a
// 交换完之后把temp到最后升序(直接取反)
// nums = nums.slice(0, temp).concat(nums.slice(temp).reverse())
for (let i = temp; i < temp + Math.floor((nums.length - temp) / 2); i++) {
let p = nums[i]
nums[i] = nums[temp + nums.length - i - 1]
nums[temp + nums.length - i - 1] = p
}
};
二.字符串
关键词:双指针 滑动窗口
1.无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
【LeetCode 直通车】:3 无重复字符的最长子串(中等)
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
let window = {};
let left = 0, right = 0;
let maxLen = 0, maxStr = '';
while (right < s.length) {
let c = s[right];
right++;
if (window[c]) window[c]++;
else window[c] = 1
while (window[c] > 1) {
let d = s[left];
left++;
window[d]--;
}
if (maxLen < right - left) {
maxLen = right - left;
}
}
return maxLen;
};
2.最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
【LeetCode 直通车】:5 最长回文子串(中等)
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
if (s.length === 1) return s;
let maxRes = 0, maxStr = '';
for (let i = 0; i < s.length; i++) {
let str1 = palindrome(s, i, i);
let str2 = palindrome(s, i, i + 1);
if (str1.length > maxRes) {
maxStr = str1;
maxRes = str1.length;
}
if (str2.length > maxRes) {
maxStr = str2;
maxRes = str2.length;
}
}
return maxStr;
};
function palindrome(s, l, r) {
while (l >= 0 && r < s.length && s[l] === s[r]) {
l--;
r++;
}
return s.slice(l + 1, r);
}
3.回文数
给你一个整数 x
,如果 x
是一个回文整数,返回 true
;否则,返回 false
。
【LeetCode 直通车】:9 回文数
function isPalindrome (x) {
var str=x.toString();
if((str.split('').reverse().join(''))===str){
return true;
}else{
return false;
}
};
4.最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
【LeetCode 直通车】:14 最长公共前缀(简单)
/**
* @param {string[]} strs
* @return {string}
*/
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;
};
5.最小覆盖子串
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
【LeetCode 直通车】:76 最小覆盖子串(困难)
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let need = {}, window = {};
for (let c of t) {
if (!need[c]) need[c] = 1;
else need[c]++;
}
let left = 0, right = 0;
let valid = 0, len = Object.keys(need).length;
let minLen = s.length + 1, minStr = '';
while (right < s.length) {
const d = s[right];
right++;
if (!window[d]) window[d] = 1;
else window[d]++;
if (need[d] && need[d] === window[d]) {
valid++;
}
console.log('left - right', left, right);
while (valid === len) {
if (right - left < minLen) {
minLen = right - left;
minStr = s.slice(left, right);
}
console.lo
let c = s[left];
left++;
window[c]--;
if (need[c] && window[c] < need[c]) {
valid--;
}
}
}
return minStr;
};
6.实现 strStr()
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
【LeetCode 直通车】:28 实现 strStr() (简单)
var strStr = function(haystack, needle) {
return haystack.indexOf(needle)
};
三.链表
1.两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
【LeetCode 直通车】:2 两数相加(中等)
var addTwoNumbers = function(l1, l2) {
let carry= 0;
let pre = point = new ListNode();
while(l1 || l2){
point.next = new ListNode();
point = point.next;
let sum = 0;
if(l1){
sum += l1.val;
l1 = l1.next;
}
if(l2){
sum += l2.val;
l2 = l2.next;
}
sum = sum + carry;
point.val = sum % 10;
carry = (sum / 10) | 0;
}
if(carry) point.next = new ListNode(carry);
return pre.next;
};
2.删除链表的倒数第 N 个结点
【LeetCode 直通车】:19 删除链表的倒数第 N 个结点(中等)
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
var removeNthFromEnd = function(head, n) {
let slow = slowCopy = fast = new ListNode();
slow.next = head;
while(n--){
fast = fast.next;
}
while(fast.next){
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return slowCopy.next;
};
3.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
【LeetCode 直通车】:21 合并两个有序链表(简单)
var mergeTwoLists = function(l1, l2) {
const prehead = new ListNode(-1);
let prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 === null ? l2 : l1;
return prehead.next;
};
4.合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
【LeetCode 直通车】:23 合并K个升序链表(复杂)
var mergeKLists = function (lists) {
const list = [];
for (let i = 0; i < lists.length; i++) {
let node = lists[i];
while (node) {
list.push(node.val);
node = node.next;
}
}
list.sort((a, b) => a - b);
const res = new ListNode();
let now = res;
// console.log(list)
for (let i = 0; i < list.length; i++) {
now.next = new ListNode(list[i]);
now = now.next;
}
return res.next;
};
5.两两交换链表中的结点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
【LeetCode 直通车】:24 两两交换链表中的结点(中等)
var swapPairs = function(head) {
const dummyHead = new ListNode(0);
dummyHead.next = head;
let temp = dummyHead;
while (temp.next !== null && temp.next.next !== null) {
const node1 = temp.next;
const node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyHead.next;
};
6.K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
【LeetCode 直通车】:25 合并K个升序链表(困难)
var reverseKGroup = function(head, k) {
let a = head, b = head;
for (let i = 0; i < k; i++) {
if (b == null) return head;
b = b.next;
}
const newHead = reverse(a, b);
a.next = reverseKGroup(b, k);
return newHead;
};
function reverse(a, b) {
let prev = null, cur = a, nxt = a;
while (cur != b) {
nxt = cur.next;
cur.next = prev;
prev = cur;
cur = nxt;
}
return prev;
}
四.栈和队列
1.有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
【LeetCode 直通车】:20 有效的括号(简单)
var isValid = function (s) {
const stack = [];
for (let i = 0; i < s.length; i++) {
let c = s[i];
switch (c) {
case '(':
stack.push(')');
break;
case '[':
stack.push(']');
break;
case '{':
stack.push('}');
break;
default:
if (c !== stack.pop()) {
return false;
}
}
}
return stack.length === 0;
};
2.最长有效括号
给你一个只包含 '('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
【LeetCode 直通车】:32 最长有效括号(困难)
const longestValidParentheses = (s) => {
let maxLen = 0;
const stack = [];
stack.push(-1);
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c == '(') { // 左括号的索引,入栈
stack.push(i);
} else { // 遍历到右括号
stack.pop(); // 栈顶的左括号被匹配,出栈
if (stack.length) { // 栈未空
const curMaxLen = i - stack[stack.length - 1]; // 计算有效连续长度
maxLen = Math.max(maxLen, curMaxLen); // 挑战最大值
} else { // 栈空了
stack.push(i); // 入栈充当参照
}
}
}
return maxLen;
};
五.深度遍历
1.括号生成
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
【LeetCode 直通车】:22 括号生成(中等)
var generateParenthesis = function (n) {
let res = [];
function dfs(str, lc, rc) {
if (Math.max(lc, rc) > n || lc < rc) {
return;
}
if (rc === n) {
res.push(str);
return;
}
dfs(str + '(', lc + 1, rc);
dfs(str + ')', lc, rc + 1);
}
dfs('', 0, 0);
return res;
};
2.串联所有单词的子串
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
【LeetCode 直通车】:30 括号生成(中等)
var findSubstring = function(s, words) {
const len = words[0].length
const res = []
// 循环, 循环的次数为字符串长度减去words所有字符串总和长度-1, 因为这个长度后面的字符串肯定不能拼接成words数组的数据
for (let i = 0; i <= s.length - words.length * len; i++) {
const wordsCopy = [...words]
// 深度优先遍历
dfs(wordsCopy, s.substring(i), i)
}
return res
function dfs(arr, s, start) {
// 递归的结束条件为数组的长度为0, 或者进不去下方的判断
if (arr.length === 0) return res.push(start)
// 从字符串开始剪切固定长度字符串, 去words中查找, 如果找不到, 结束, 如果找到了 继续往下查找
const str = s.substr(0, len)
const index = arr.findIndex((item) => item === str)
if (index > -1) {
// 递归查找之前需要将已经使用过的数组索引删除, 字符串也需要删除已经判断过的
arr.splice(index, 1)
dfs(arr, s.substring(len), start)
}
}
}