详细解说请看视频JS老毕:人人都能看得懂的Leetcode力扣刷题教程合集
边看视频边记录笔记!!!部分题目在视频中无!
文章目录
- LeetCode第1题:1. 两数之和
- LeetCode第2题:2. 两数相加
- LeetCode第3题:3.无重复字符的最长子串
- LeetCode第5题:5. 最长回文子串
- LeetCode第11题:11. 盛最多水的容器
- LeetCode第15题:15. 三数之和
- LeetCode第17题:17. 电话号码的字母组合
- LeetCode第19题:19. 删除链表的倒数第 N 个结点
- LeetCode第20题:20. 有效的括号
- LeetCode第21题:21. 合并两个有序链表
- LeetCode第24题:24. 两两交换链表中的节点
- LeetCode第49题:49. 字母异位词分组
- LeetCode第53题:53. 最大子序和
- LeetCode第54题:54. 螺旋矩阵
- LeetCode第55题:55. 跳跃游戏
- LeetCode第56题:56. 合并区间
- LeetCode第62题:62. 不同路径
- LeetCode第66题:66. 加一
- LeetCode第70题:70. 爬楼梯
- LeetCode第73题:73. 矩阵置零
- LeetCode第83题:83. 删除排序链表中的重复元素
- LeetCode第206题:206. 反转链表
- LeetCode第92题:92. 反转链表 II
- LeetCode第121题:121. 买卖股票的最佳时机
- LeetCode第122题:122. 买卖股票的最佳时机 II
- LeetCode第123题:123. 买卖股票的最佳时机 III
- LeetCode第125题:125. 验证回文串
- LeetCode第134题:134. 加油站
- LeetCode第135题:135. 分发糖果
- LeetCode第141题:141. 环形链表
- LeetCode第142题:142. 环形链表 II
- LeetCode第152题:152. 乘积最大子数组
- LeetCode第153题:153. 寻找旋转排序数组中的最小值
- LeetCode第160题:160. 相交链表
- LeetCode第187题:187. 重复的DNA序列
- LeetCode第198题:198. 打家劫舍
- LeetCode第200题:200. 岛屿数量
- LeetCode第215题:215. 数组中的第K个最大元素
- LeetCode第217题:217. 存在重复元素
- LeetCode第219题:219. 存在重复元素 II
- LeetCode第238题:238. 除自身以外数组的乘积
- LeetCode第242题:242. 有效的字母异位词
- LeetCode第283题:283. 移动零
- LeetCode第328题:328. 奇偶链表
- LeetCode第349题:349. 两个数组的交集
- LeetCode第419题:419. 甲板上的战舰
- LeetCode第445题:445. 两数相加 II
- LeetCode第509题:509. 斐波那契数
- LeetCode第680题:680. 验证回文字符串 Ⅱ
- LeetCode第695题:695. 岛屿的最大面积
- LeetCode第704题:704. 二分查找
- LeetCode第733题:733. 图像渲染
- LeetCode第796题:796. 旋转字符串
- LeetCode第836题:836. 矩形重叠
- LeetCode第844题:844. 比较含退格的字符串
- LeetCode第876题:876. 链表的中间结点
- LeetCode第904题:904. 水果成篮
- LeetCode第905题:905. 按奇偶排序数组
- LeetCode第922题:922. 按奇偶排序数组 II
LeetCode第1题:1. 两数之和
解法1:时间复杂度为O(n*n) 空间复杂度为O(1)
var twoSum = function(nums, target) {
//给定一个nums,给定一个target,设定1个空数组用来存放下标
//let result = [];
//在nums中遍历,找到和为target的两个数,且这两个数不能为同一个元素,即下标不可相同
for(let i=0;i<nums.length;i++){
for(let j=i+1;j<nums.length;j++){
if(nums[i] + nums[j] == target && i!=j){
//返回这两个数的下标的数组
return [i,j];
}
}
}
};
解法2:时间复杂度为O(n) 空间复杂度为O(n)
var twoSum = function(nums,target){
const map = new Map();
for(let i = 0;i < nums.length;i++){
const complement = target - nums[i];
if(map.has(complement)){
return [map.get(complement),i];
}else{
map.set(nums[i],i);
}
}
return [];
}
LeetCode第2题:2. 两数相加
代码:
var addTwoNumbers = function(l1,l2){
let dummy = new ListNode();
let curr = dummy;
let carry = 0;
while(l1 !==null || l2 !==null){
let sum = 0;
if(l1){
sum += l1.val;
l1 = l1.next;
}
if(l2){
sum += l2.val;
l2 = l2.next;
}
sum +=carry;
curr.next = new ListNode(sum % 10);
carry = Math.floor(sum/10);
curr = curr.next;
}
if(carry>0){
curr.next = new ListNode(carry);
}
return dummy.next;
}
LeetCode第3题:3.无重复字符的最长子串
思路:
方法:sliding window(滑动窗口算法)
1.创建一个set
2.两个指针,第一个指向字符串的开头(j),第二个随着for循环遍历字符串(i)
3.如果set里没有s(i),说明目前为止还没有重复的字符,把s(i)添加到set里,然后更新最大不重复字符的数量
4.如果set里有s(i),则从set里开始删除s(i),并且递增j,再检查set里是否有s(i)
5.重复步骤3和4,直到遍历完整个字符串
代码:
var lengthOfLongestSubstring = function(s){
const set = new Set();
let i = 0,j = 0,maxLength = 0;
if(s.length === 0){
return 0;
}
for(i;i<s.length;i++){
if(!set.has(s[i])){
set.add(s[i]);
maxLength = Math.max(maxLength,set.size)
}else{
while(set.has(s[i])){
set.delete(s[j]);
j++;
}
set.add(s[i]);
}
}
return maxLength;
};
LeetCode第5题:5. 最长回文子串
思路:
有三种情况:
1.长度小于2,直接返回
2.奇数个,有中间字符
3.偶数个,没有中间字符
1.如果字符串长度小于2,直接返回字符串
2.定义两个变量,一个start存储当前找到的最大回文字符串的起始位置,另一个maxLength记录字符串的长度(终止位置就是start+maxLength)
3.创建一个helper function,判断左边和右边是否越界,同时最左边的字符是否等于最右边的字符。
如果以上三个条件都满足,则判断是否需要更新回文字符串最大长度及最大字符串的起始位置。然后将left--、right++,继续判断,直到不满足三个条件之一
4.遍历字符串,每个位置调用helper function两遍,第一遍检查i-1,i+1,第二遍检查i、i+1
(为什么要检查两遍?答:奇数/偶数)
代码:
var longestPalindrome = function(s){
if(s.length<2){
return s;
}
let start = 0;
let maxLength = 1;
function expandAroundCenter(left,right){
while(left>=0 && right<s.length && s[left]===s[right]){
if(right - left + 1 > maxLength){
maxLength = right - left +1;
start = left;
}
left--;
right++;
}
}
for(let i=0;i<s.length;i++){
expandAroundCenter(i-1,i+1);
expandAroundCenter(i,i+1);
}
return s.substring(start,start+maxLength);
};
LeetCode第11题:11. 盛最多水的容器
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let left = 0, right = height.length - 1;
let max = 0;
while(left < right){
let maxAll = (right - left) * Math.min(height[left],height[right]);
if(height[left] <= height[right]){
left++;
max = Math.max(max,maxAll);
}else{
right--;
max = Math.max(max,maxAll);
}
}
return max;
};
//简化版
var maxArea = function(height) {
let left = 0;
let right = height.length - 1;
let max = 0;
while(left < right) {
let temp = (right - left) * Math.min(height[left], height[right]);
max = Math.max(temp, max);
if (height[left] <= height[right]) {
left++;
} else {
right--;
}
}
return max;
};
LeetCode第15题:15. 三数之和
思路:
1.给数组排序
2.遍历数组,从0遍历到length-2
为什么?答:防止越界
3.如果当前的数字等于前一个数字,则跳过这个数
为什么?答:避免重复数组出现
4.如果数字不同,则设置start= i+1,end=length-1,查看i,start,end三个数的和比0大还是小,
· 如果比0小,start++,
· 如果比0大,end--
· 如果等于0,则把这三个数添加到结果里
5.返回结果
代码:
var threeSum = function(nums) {
const result = [];
nums.sort((a,b)=>{
return a-b;
})
for(let i=0;i<nums.length-2;i++){
if(i===0 || nums[i] !== nums[i-1]){
let start = i+1,end = nums.length-1;
while(start < end){
if(nums[i] + nums[start] + nums[end] === 0){
resule.push([nums[i],nums[start],nums[end]]);
start++;
end--;
while(start < end && nums[start] === nums[start-1]){
start++;
}
while(start < end && nums[end] === nums[end+1]){
end--;
}
}else if(nums[i] + nums[start] + nums[end] < 0){
start++;
}else{
end--;
}
}
}
}
return result;
};
LeetCode第17题:17. 电话号码的字母组合
代码:
// dfs,回溯算法
var letterCombinations = function(digits) {
if (digits.length === 0) return [];
let res = [];
const map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'};
const dfs = (curStr, i) => {
if (i > digits.length -1) {
res.push(curStr);
return;
}
const letters = map[digits[i]]
for (letter of letters) {
dfs(curStr + letter, i+1);
}
}
dfs('', 0);
return res;
};
LeetCode第19题:19. 删除链表的倒数第 N 个结点
思路:
1.在第一个结点之前设置一个dummy结点,避免只有一个元素无法操作
2.设置两个指针,分别为n1,n2,开始都指向dummy
3.两种方法
1)将n2结点移动n个距离,然后n1,n2同时向后移,
当n2移动到最后一个元素即n2.next为null的时候,
删除当前n1.next即n1.next = n1.next.next
for(let i=0;i<n;i++){
n2 = n2.next;
}
while(n2.next !== null){
n1 = n1.next;
n2 = n2.next;
}
2)将n2结点移动n+1个距离,然后n1,n2同时向后移,
当n2为null时, 删除当前n1.next即n1.next = n1.next.next
for(let i=0;i<=n;i++){
n2 = n2.next;
}
while(n2 !== null){
n1 = n1.next;
n2 = n2.next;
}
代码:
var removeNthFromEnd = function(head, n) {
let dummy = new ListNode();
dummy.next = head;
let n1 = dummy;
let n2 = dummy;
for(let i=0;i<=n;i++){
n2 = n2.next;
}
while(n2 !== null){
n1 = n1.next;
n2 = n2.next;
}
n1.next = n1.next.next;
return dummy.next;
};
LeetCode第20题:20. 有效的括号
思路:
1.创建一个HashMap,把括号配对放进去
2.创建一个stack(array、栈),for循环遍历字符串,对于每一个字符,如果map里有这个key,那说明它是个左括号,从map里取得相对应的右括号(为什么?),把它push进stack里。否则的话,它就是右括号,需要pop出stack里的第一个字符,然后看它是否等于当前的字符。如果不相符,则返回false
3.循环结束后,如果stack不为空,说明还剩一些左括号没有被闭合,返回false。否则返回true。
代码:
var isValid = function(s) {
const mappings = new Map();
mappings.set("(" , ")");
mappings.set("[" , "]");
mappings.set("{" , "}");
const stack = [];
for(let i=0;i<s.length;i++){
if(mappings.has(s[i])){
stack.push(mappings.get(s[i]));
}else{
if(stick.pop() !== s[i]){
return false;
}
}
}
if(stack.length !== 0){
return false;
}
return true;
};
LeetCode第21题:21. 合并两个有序链表
代码:
var mergeTwoLists = function(l1, l2) {
let curr = new ListNode();
let dummy = curr;
while(l1 !== null && l2 !== null){
if(l1.val < l2.val){
curr.next = l1;
l1 = l1.next;
}else{
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
if(l1 !== null){
curr.next = l1;
}
if(l2 !== null){
curr.next = l2;
}
return dummy.next;
};
LeetCode第24题:24. 两两交换链表中的节点
思路:
- n1 = p.next;
- n2 = p.next.next;
- p.next = n2;
- n1.next = n2.next;
- n2.next = n1;
- p = n1;
代码:
var swapPairs = function(head) {
let dummy = new ListNode();
dummy.next = head;
let current = dummy;
// n1,n2均不为0
while(current.next !== null && current.next.next !== null){
let n1 = current.next;
let n2 = current.next.next;
current.next = n2;
n1.next = n2.next;
n2.next = n1;
current = n1;
}
return dummy.next;
};
LeetCode第49题:49. 字母异位词分组
思路:
1.检查是否为空数组
2.建立一个长度为26的数组,起始值为0
3.遍历所有字符串,将字符出现的频率放到数组的对应位置里(ASCII)
4.遍历数组,按照相同字母出现频率表进行分组归类(hashMap)
5.遍历map,将结果返回
纠错:join里面的分隔符改成别的例如 . -
测试用例如果是【bdddddddddd, bbbbbbbbbbc】的话key会相同 会报错
【
0, 1, 0, 10, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0
】
010100000000000000000000000
【
0, 10, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0
】
010100000000000000000000000
Map(1) {
010100000000000000000000000 = 【 bdddddddddd, bbbbbbbbbbc 】
}
代码:
var groupAnagrams = function(strs) {
if(strs.length === 0){
return [];
}
const map = new Map();
for(const str of strs){
const characters = Array(26).fill(0);
for(let i=0;i<str.length;i++){
//console.log(str[i]);
const ascii = str.charCodeAt(i) - 97;
//console.log(str.charCodeAt(i));
//console.log(ascii);
characters[ascii]++;
}
//const key = characters.join("");
const key = characters.join(".");
if(map.has(key)){
//map.set(key,map.get(key.push(str)))
map.set(key,[...map.get(key),str]);
}else{
map.set(key,[str]);
}
}
const result = [];
for(const arr of map){
result.push(arr[1]); // 0是键,1是键值
}
return result;
};
LeetCode第53题:53. 最大子序和
思路:
两次判断:
1.将新的数加入到前面的数组中,还是舍弃之前的数组新开一个数组
2.判断最大值
代码:
var maxSubArray = function(nums) {
const memo = [];
memo[0] = nums[0];
for(let i=1;i<nums.length;i++){
memo[i] = Math.max(nums[i] + memo[i-1],nums[i]);
}
let max = nums[0];
for(let i=1;i<memo.length;i++){
max = Math.max(max,memo[i]);
}
return max;
};
LeetCode第54题:54. 螺旋矩阵
思路:
1.如果数组为空,返回空数组
2.定义4个边界以及当前方向
3.当左边界小于等于右边界,且上边界小于等于下边界时,执行while循环按照右,下,左,上的顺序,依次将路径上的字符添加到结果里
4.while循环结束后,返回结果
代码:
var spiralOrder = function(matrix) {
if(matrix.length === 0){
return [];
}
let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;
let direction = 'right';
let result = [];
/*
[1 , 2 , 3 , 4],
[5 , 6 , 7 , 8],
[9 , 10, 11 ,12]
*/
while(left <= right && top <= bottom){
if(direction === 'right'){
for(let i=left;i<=right;i++){
result.push(matrix[top][i]);
}
top++;
direction = 'down';
}else if(direction === 'down'){
for(let i=top;i<=bottom;i++){
result.push(matrix[i][right]);
}
right--;
direction = 'left';
}else if(direction === 'left'){
for(let i=right;i>=left;i--){
result.push(matrix[bottom][i]);
}
bottom--;
direction = 'top';
}else if(direction === 'top'){
for(let i=bottom;i>=top;i--){
result.push(matrix[i][left]);
}
left++;
direction = 'right';
}
}
return result;
};
LeetCode第55题:55. 跳跃游戏
解法1:递归版(动态规划)
var canJump = function(nums) {
const totalLength = nums.length;
const memo = Array(totalLength).fill(0);
//dp
memo[totalLength - 1] = 1;
function jump(position){
if(memo[position] === 1){
return true;
}else if(memo[position] === -1){
return false;
}
const maxJump = Math.min(position + nums[position],totalLength - 1)
for(let i=position+1;i<=maxJump;i++){
const jumpReault = jump(i);
if(jumpReault === true){
memo[position] = 1;
return true;
}
}
memo[position] = -1;
return false;
}
let result = jump(0);
return result;
};
解法2:优化版1(bottom-up)
//bottom-up思路:
//从后往前判断,只要倒数第二个能到达最后一个,
//就判断从前面哪个能到倒数第二个,再继续依次向前推,直到数组最开始
var canJump = function(nums) {
const totalLength = nums.length;
const memo = Array(totalLength).fill(0);
//dp
memo[totalLength - 1] = 1;
for(let i=totalLength - 2;i>=0;i--){
const maxJump = Math.min(totalLength-1,i+nums[i]);
for(let j=i+1;j<=maxJump;j++){
if(memo[j] === 1){
memo[i] = 1;
break;
}
}
}
if(memo[0] === 1){
return true;
}else{
return false;
}
};
解法3:优化版2(贪心算法)
//贪心算法思路:
//依旧从后往前判断,刚开始的maxJump=nums.length - 1,
//判断索引+该索引对应的值是否大于maxJump,
//大于则maxJump就为该索引i,
//循环结束如果maxJump为0,则说明了到达了第一个数,证明成功了
var canJump = function(nums) {
let maxJump = nums.length - 1;
for(let i=nums.length-2;i>=0;i--){
if(i+nums[i] >= maxJump){
maxJump = i;
}
}
return maxJump === 0;
};
LeetCode第56题:56. 合并区间
思路:
1.将数组中的区间按照起始位置排序
2.用curr数据记录当前合并的最大区间,遍历数组中的每一个区间,如果当前区间的起始位置小于curr的终点位置,则可以继续合并,所以合并并更新curr的起始和终止位置。如果当前区间的起始位置大于curr的终止位置,则无法合并所以将curr加入到result里,并用当前的区间替换curr的值
代码:
var merge = function(intervals) {
if(intervals.length < 2){
return intervals;
}
intervals.sort((a,b)=>{
return a[0] - b[0];
})
let curr = intervals[0];
let result = [];
for(let interval of intervals){
if(curr[1] >= interval[0]){
curr[1] = Math.max(curr[1],interval[1]);
}else{
result.push(curr);
curr = interval;
}
}
if(curr.length !== 0){
result.push(curr);
}
return result;
};
LeetCode第62题:62. 不同路径
代码:
//动态规划
//到达每个格子的路劲总数等于到达该格子上面和左面的路径之和
//依次累加
var uniquePaths = function(m, n) {
const memo = [];
//建立一个二维数组
for(let i=0;i<n;i++){
memo.push([]);
}
for(let row=0;row<n;row++){
memo[row][0] = 1;
}
for(let col=0;col<m;col++){
memp[0][col] = 1;
}
for(let row=1;row<n;row++){
for(let col=1;col<m;col++){
memo[row][col] = memo[row-1][col] + memo[row][col-1];
}
}
return memo[n-1][m-1];
};
LeetCode第66题:66. 加一
代码:
var plusOne = function(digits) {
for(let i=digits.length-1;i>=0;i--){
if(digits[i] !== 9){
digits[i]++;
return digits
}else{
digits[i] = 0;
}
}
const result = [1,...digits];
return result;
};
LeetCode第70题:70. 爬楼梯
代码:
var climbStairs = function(n) {
//memo[i-2] = 2
//memo[i-1] = 3
//memo[i] = memo[i-1] + memo[i-2] = 5
const memo = [];
memo[1] = 1;
memo[2] = 2;
//memo[3] = 3;
//memo[4] = 5;
for(let i=3;i<=n;i++){
memo[i] = memo[i-2] + memo[i-1];
}
return memo[n];
};
LeetCode第73题:73. 矩阵置零
思路:
1.检查并标记第一行和第一列是否有0(firstColHasZero和firstRowHashZero)
2.使用第一行和第一列,来标记其余行列是否含有0
3.接下来,利用第一行和第一类的标0情况,将matrix中的数字标0
4.最后,处理第一行和第一列
如果firstColHasZero等于true,将第一列全设为0
如果firstRowHashZero等于true,将第一行全设为0
代码:
var setZeroes = function(matrix) {
let firstColHashZero = false;
let firstRowHashZero = false;
//判断第一列是否有0
for(let i=0;i<matrix.length;i++){
if(matrix[i][0] === 0){
firstColHashZero = true;
}
}
//判断第一行是否有0
for(let i=0;i<matrix.length;i++){
if(matrix[0][i] === 0){
firstRowHashZero = true;
}
}
//排除第一行和第一列,标记其余行列是否含有0,
//有0则将该0所对应的行、列所在的第一行、第一列数字标0
for(let row=1;row<matrix.length;row++){
for(let col=1;col<matrix[0].length;col++){
if(matrix[row][col] === 0){
matrix[row][0] = 0;
matrix[0][col] = 0;
}
}
}
//利用第一行和第一类的标0情况,将matrix中的数字标0
for(let row=1;row<matrix.length;row++){
for(let col=1;col<matrix[0].length;col++){
if(matrix[row][0] === 0 || matrix[0][col] === 0){
matrix[row][col] = 0;
}
}
}
// 如果firstColHasZero等于true,将第一列全设为0
if(firstColHashZero){
for(let i=0;i<matrix.length;i++){
matrix[i][0] = 0;
}
}
//如果firstRowHashZero等于true,将第一行全设为0
if(firstRowHashZero){
for(let i=0;i<matrix[o].length;i++){
matrix[0][i] = 0;
}
}
return matrix;
};
LeetCode第83题:83. 删除排序链表中的重复元素
思路:
1.建立一个指针遍历整个链表
2.如果前一个和后一个相等,则删除下一个指针,否则指针后移
代码:
var deleteDuplicates = function(head) {
let current = head;
while(current !== null && current.next !==null){
if(current.val === current.next.val){
current.next = current.next.next;
}else{
current = current.next;
}
}
return head;
};
LeetCode第206题:206. 反转链表
解法1:普通版
var reverseList = function(head) {
let prev = null;
let curr = head;
let next = head;
while(curr !== null){
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
};
解法2:优化版
var reverseList = function(head) {
let prev = null;
let curr = head;
while(curr !== null){
[curr.next,prev,curr] = [prev,curr,curr.next];
}
return prev;
};
LeetCode第92题:92. 反转链表 II
解法1:普通版
//思路:
//1、反转m至n之间的链表
//2、将反转后的链表与原链表拼接
var reverseBetween = function(head, left, right) {
let prev = null;
let curr = head;
let next = head;
for(let i=1;i<left;i++){
prev = curr;
curr = curr.next;
}
let prev2 = prev;
let curr2 = curr;
for(let i=left;i<=right;i++){
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
if(prev2 !== null){
prev2.next = prev;
}else{
head = prev;
}
curr2.next = curr;
return head;
};
解法2:简化版
//思路:
//1、反转m至n之间的链表
//2、将反转后的链表与原链表拼接
var reverseBetween = function(head, left, right) {
let prev = null;
let curr = head;
for(let i=1;i<left;i++){
prev = curr;
curr = curr.next;
}
let prev2 = prev;
let curr2 = curr;
for(let i=left;i<=right;i++){
[curr.next,prev,curr] = [prev,curr,curr.next]
}
if(prev2 !== null){
prev2.next = prev;
}else{
head = prev;
}
curr2.next = curr;
return head;
};
LeetCode第121题:121. 买卖股票的最佳时机
思路:
1.设定两个变量,
一个用来存储目前所在点左半边的最小点,
另一个用来存储目前利润的最大值
2.遍历数组
首先判断minPrice是否是最小值
再判断maxProfit是否是最大值
代码:
var maxProfit = function(prices) {
if(prices.length === 0){
return 0;
}
let minPrice = prices[0];
let maxProfit = 0;
for(let i=0;i<prices.length;i++){
if(prices[i] < minPrice){
minPrice = prices[i];
}else if(prices[i] - minPrice > maxProfit){
maxProfit = prices[i] - minPrice;
}
}
return maxProfit;
};
LeetCode第122题:122. 买卖股票的最佳时机 II
思路:
思路1:
1.向上的曲线,底端买入,顶端卖出
2.向下的曲线不买也不卖
思路2:
将每一项和它的后一项进行对比,如果小于后一项,
就把之差加到利润中
代码:
var maxProfit = function(prices) {
if(prices.length === 0){
return 0;
}
//思路1!!!
/* let profit = 0, valley = prices[0], peak = prices[0];
let i = 0;
while(i<prices.length - 1){
while(i<prices.length - 1 && prices[i] >= prices[1+1]){
i++;
}
valley = prices[i];
while(i<prices.length - 1 && prices[i] <= prices[i+1]){
i++;
}
peak = prices[i];
profit += peak - valley;
} */
//思路2!!!
let profit = 0;
for(let i=0;i<prices.length - 1;i++){
if(prices[i] < prices[i+1]){
profit += prices[i+1] - prices[i];
}
}
return profit;
};
LeetCode第123题:123. 买卖股票的最佳时机 III
代码:
var maxProfit = function(prices) {
if(prices.length === 0){
return 0;
}
//二维数组,行,列
const dp = Array.from(Array(3),()=>new Array(prices.length));
//将第一行,第一列都设为0
for(let i=0;i<prices.length;i++){
dp[0][i] = 0;
}
for(let i=0;i<3;i++){
dp[i][0] = 0;
}
for(let i=1;i<3;i++){
let maxProfit = -prices[0];
for(let j=1;j<prices.length;j++){
dp[i][j] = Math.max(dp[i][j-1],prices[j]+maxProfit);
maxProfit = Math.max(maxProfit,dp[i-1][j] - prices[j]);
}
}
return dp[2][prices.length-1];
};
LeetCode第125题:125. 验证回文串
思路:
1.用正则表达式去掉非数字和字母
2.如果字符串长度小于2,返回true
3.定义两个指针,一个在字符串开头,一个在字符串结尾
4.建议一个while循环,当left<right时执行循环,如果在任意地点s[left] !== s[right],return false,否则left++,right--,继续执行循环
5.当循环完成后都没有return false,则return true
代码:
var isPalindrome = function(s) {
s = s.toLowerCase().replace(/[\W_]/g,"");
if(s.length < 2){
return true;
}
let left = 0;
let right = s.length - 1;
while(left < right){
if(s[left] !== s[right]){
return false;
}
left++;
right--;
}
return true;
};
LeetCode第134题:134. 加油站
思路:
1.首先将所有加油站的油加起来,再把需要消耗的油全部加起来,如果前者 小于后者,则能走完一圈,继续算法,否则直接返回-1
2.从某个点开始逐步判断,如果走不通则从走不通的那个点重新开始判断
代码:
var canCompleteCircuit = function(gas, cost) {
let totalGas = 0;
let totalCost = 0;
for(let i=0;i<gas.length;i++){
totalCost += cost[i];
totalGas += gas[i];
}
if(totalCost > totalGas){
return -1;
}
let currentGas = 0;
let start = 0;
for(let i=0;i<gas.length;i++){
currentGas = currentGas - cost[i] + gas[i];
if(currentGas < 0){
currentGas = 0;
start = i + 1;
}
}
return start;
};
LeetCode第135题:135. 分发糖果
// 贪心算法
var candy = function(ratings) {
const n = ratings.length;
const left = new Array(n).fill(0);
//从左向右遍历,找出右边比左边大的
for(let i=0; i<n; i++) {
if (i > 0 && ratings[i] > ratings[i-1]) {
left[i] = left[i-1] + 1;
} else {
left[i] = 1;
}
}
//从右向左遍历,找出左边比右边大的
let right = 0, res = 0;
for(let i=n-1; i>-1;i--) {
if (i < n-1 && ratings[i] > ratings[i+1]) {
right++;
} else {
right = 1;
}
res += Math.max(left[i], right);
}
return res;
};
LeetCode第141题:141. 环形链表
思路:
1.设定两个指针,快指针每次走两步,慢指针每次走一步
2.如果存在环,快指针和慢指针总会重合
3.慢指针比快指针快几步,快指针就能几步追上慢指针
代码:
var hasCycle = function(head) {
if(head === null){
return false;
}
let slow = head;
let fast = head;
while(fast.next !== null && fast.next.next !== null){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
return true;
}
}
return false;
};
LeetCode第142题:142. 环形链表 II
代码:
var detectCycle = function(head) {
if(head === null){
return null;
}
let slow = head;
let fast = head;
let isCycle = false;
while(fast.next !== null && fast.next.next !== null){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
isCycle = true;
break;
}
}
if(!isCycle){
return null;
}
fast = head;
while(slow !== fast){
slow = slow.next;
fast = fast.next;
}
return fast;
};
LeetCode第152题:152. 乘积最大子数组
代码:
var maxProduct = function(nums) {
const maxProductMemo = [];
const minProductMemo = [];
maxProductMemo[0] = nums[0];
minProductMemo[0] = nums[0];
let max = nums[0];
for(let i=1;i<nums.length;i++){
//乘minProductMemo的原因在于nums[i]可能是个负值,
//minProductMemo也可能是负值,负负得正
maxProductMemo[i] = Math.max(nums[i],nums[i]*maxProductMemo[i-1],nums[i]*minProductMemo[i-1]);
minProductMemo[i] = Math.min(nums[i],nums[i]*maxProductMemo[i-1],nums[i]*minProductMemo[i-1]);
max = Math.max(max,maxProductMemo[i]);
}
return max;
};
LeetCode第153题:153. 寻找旋转排序数组中的最小值
思路:
1.如果数组长度为1,返回唯一的一个数
2.定义两个指针,第一个left指向数组开头,第二个right指向数组结尾
3.检查数组是否被翻转,如果没有,则返回数组里的第一个数
4.当left小于right时,取中间作为mid进行二分搜索,如果mid的左边一个数大于mid,或者mid右边的一个数小于mid,则返回mid
5.否则的话,如果left所在的数小于mid,则将left右移至mid+1位置(砍掉左半边)
6.否则的话,将right左移至mid-1的位置(砍掉右半边)
代码:
var findMin = function(nums) {
if(nums.length === 1){
return nums[0];
}
let left = 0, right = nums.length - 1;
if(nums[right] > nums[0]){
return nums[0];
}
while(left < right){
let mid = Math.floor(left + (right - left) / 2);
if(nums[mid] > nums[mid + 1]){
return nums[mid + 1];
}
if(nums[mid - 1] > nums[mid]){
return nums[mid];
}
if(nums[mid] > nums[left]){
left = mid + 1;
}else{
right = mid - 1;
}
}
};
LeetCode第160题:160. 相交链表
代码:
var getIntersectionNode = function(headA, headB) {
let n1 = headA;
let n2 = headB;
while(n1 !== n2){
if(n1 === null){
n1 = headB;
}else{
n1 = n1.next;
}
if(n2 === null){
n2 = headA;
}else{
n2 = n2.next;
}
}
return n1;
};
LeetCode第187题:187. 重复的DNA序列
解法1:
var findRepeatedDnaSequences = function(s) {
const map = new Map();
const result = [];
let i = 0;
while(i + 10 <= s.length){
const dna = s.substring(i,i+10);
if(map.get(dna) === undefined){
map.set(dna,1);
}else if(map.get(dna) === 1){
map.set(dna,2);
result.push(dna);
}else{
map.get(dna,map.get(dna) + 1);
}
i++;
}
return result;
};
解法2:
var findRepeatedDnaSequences = function(s) {
const set = new Set(),result = new Set();
let i = 0;
while(i + 10 <= s.length){
const dna = s.substring(i,i+10);
if(set.has(dna)){
result.add(dna);
}else{
set.add(dna);
}
i++;
}
return Array.from(result);
};
LeetCode第198题:198. 打家劫舍
解法1:
var rob = function(nums) {
if(nums.length === 0){
return 0;
}
if(nums.length === 1){
return nums[0];
}
const memo = [];
memo[0] = nums[0];
memo[1] = Math.max(nums[0],nums[1]);
for(let i=2;i<nums.length;i++){
memo[i] = Math.max(nums[i] + memo[i-2],memo[i-1]);
}
return memo[nums.length - 1];
};
解法2:
var rob = function(nums) {
if(nums.length === 0){
return 0;
}
if(nums.length === 1){
return nums[0];
}
let prev2 = nums[0];
let prev1 = Math.max(nums[0],nums[1]);
for(let i=2;i<nums.length;i++){
const temp = Math.max(nums[i] + prev2,prev1);
prev2 = prev1;
prev1 = temp;
}
return prev1;
};
LeetCode第200题:200. 岛屿数量
代码:
//bfs -- 广度优先搜索(上下左右一次处理完,往外扩散再上下左右)
//dfs -- 深度优先搜索(上下左右,先把一条路走完,再返回去处理)
var numIslands = function(grid) {
let count = 0;
function dfs(row,col){
if(row<0 || row>=grid.length || col<0 || col>=grid[0].length || grid[row][col] === '0'){
return;
}
grid[row][col] = '0';
dfs(row-1 , col);
dfs(row+1 , col);
dfs(row, col-1);
dfs(row, col+1);
}
for(let row=0;row<grid.length;row++){
for(let col=0;col<grid[0].length;col++){
if(grid[row][col] === '1'){
count++;
dfs(row,col);
}
}
}
return count;
};
LeetCode第215题:215. 数组中的第K个最大元素
// 堆排序
LeetCode第217题:217. 存在重复元素
代码:
var containsDuplicate = function(nums) {
const set = new Set();
for(let i=0;i<nums.length;i++){
if(set.has(nums[i])){
return true;
}
set.add(nums[i]);
}
return false;
};
LeetCode第219题:219. 存在重复元素 II
代码:
var containsNearbyDuplicate = function(nums, k) {
const map = new Map();
for(let i=0;i<nums.length;i++){
if(map.has(nums[i]) && (i - map.get(nums[i]) <= k)){
return true;
}else{
map.set(nums[i],i);
}
}
return false;
};
LeetCode第238题:238. 除自身以外数组的乘积
代码:
var productExceptSelf = function(nums) {
const result = Array(nums.length).fill(1);
let product = 1;
for(let i=0;i<nums.length;i++){
result[i] = result[i] * product;
product = product * nums[i];
}
product = 1;
for(let i=nums.length - 1;i>=0;i--){
result[i] = result[i] * product;
product = product * nums[i];
}
return result;
};
LeetCode第242题:242. 有效的字母异位词
思路:
1.如果两个数组长度不一致,则返回false
2.创建一个map,用来存储每个字符出现的次数
3.对于第一个单词的每个字母,也就是s1[i],在map里将出现次数+1
对于第二个单词的每个字母,也就是s2[i],在map里将出现次数-1
4.遍历完成后,检查map里的每一个字母出现的次数是不是0,如果有一个非0的字母,返回false,否则返回true
代码:
var isAnagram = function(s, t) {
if(s.length !== t.length){
return false;
}
const map = new Map();
for(let i=0;i<s.length;i++){
if(map.has(s[i])){
map.set(s[i],map.get(s[i])+1);
}else{
map.set(s[i],1);
}
if(map.has(t[i])){
map.set(t[i],map.get(t[i])-1);
}else{
map.set(t[i],-1);
}
}
for(const letter of map){
if(letter[1] !== 0){
return false;
}
}
return true;
};
LeetCode第283题:283. 移动零
思路:
将非0的数字移到数字开头,然后将剩余的位置补上0,用i和j控制
代码:
var moveZeroes = function(nums) {
let j = 0;
for(let i=0;i<nums.length;i++){
if(nums[i] !== 0){
nums[j] = nums[i];
j++;
}
}
for(i=j;i<nums.length;i++){
nums[i] = 0;
}
return nums;
};
LeetCode第328题:328. 奇偶链表
代码:
var oddEvenList = function(head) {
if(head === null){
return null;
}
if(head.next === null){
return head;
}
let odd = head;
let even = head.next;
let evenHead = head.next;
while(even !== null && even.next !== null){
odd.next = odd.next.next;
odd = odd.next;
even.next = even.next.next;
even = even.next;
}
odd.next = evenHead;
return head;
};
LeetCode第349题:349. 两个数组的交集
代码:
//交集即nums1中有,nums2中也有的元素
var intersection = function(nums1, nums2) {
const result = new Set();
const set = new Set(nums2);
/* for(num of nums2){
set.add(num);
} */
for(num of nums1){
//数组搜索值,复杂度O(n)
//Set Map --- .has 复杂度O(1)
if(set.has(num)){
result.add(num);
}
}
return Array.from(result);
};
LeetCode第419题:419. 甲板上的战舰
解法1:把相邻的X全都转化成 .
var countBattleships = function(board) {
let result = 0;
for(let row=0;row<board.length;row++){
for(let col=0;col<board[0].length;col++){
if(board[row][col] === 'X'){
result++;
dfs(row,col);
}
}
}
function dfs(row,col){
if(row<0 || row>=board.length || col<0 || col>=board[0].length || board[row][col] !== 'X'){
return;
}
board[row][col] = '.';
dfs(row-1,col);
dfs(row+1,col);
dfs(row,col+1);
dfs(row,col-1);
}
return result;
};
解法2:不管相邻的X,继续往下进行
var countBattleships = function(board) {
let result = 0;
for(let row=0;row<board.length;row++){
for(let col=0;col<board[0].length;col++){
if(board[row][col] === 'X'){
//左面
if(row>0 && board[row-1][col] === 'X'){
continue;
}
//上面
if(col>0 && board[row][col-1] === 'X'){
continue;
}
result++;
}
}
}
return result;
};
LeetCode第445题:445. 两数相加 II
思路:
1.将这两个链表里的数都放到栈里,然后依次相加,注意存储进位
2.将第一个和作为链表的最后一项,把它的下一项设为null,即curr的初始值
3.然后把node赋值给curr,依次循环
4.检查最后的进位如果为1,继续上述操作,将1设为链表第一项
代码:
var addTwoNumbers = function(l1, l2) {
const stack1 = [], stack2 = [];
while(l1 !== null){
stack1.push(l1.val);
l1 = l1.next;
}
while(l2 !== null){
stack2.push(l2.val);
l2 = l2.next;
}
let carry = 0, curr = null;
while(stack1.length!==0 || stack2.length!==0){
let sum = 0;
if(stack1.length !== 0){
sum += stack1.pop();
// const sum = stack1.pop();
// sum = sum + num;
}
if(stack2.length !== 0){
sum += stack2.pop();
// const num = stack1.pop();
// sum = sum + num;
}
sum += carry;
const node = new ListNode(sum % 10);
carry = Math.floor(sum / 10);
node.next = curr;
curr = node;
}
if(carry !== 0){
const node = new ListNode(carry);
node.next = curr;
curr = node;
}
return curr;
};
LeetCode第509题:509. 斐波那契数
解法1:top-down
var fib = function(n) {
if(n <= 1){
return n;
}
const cache = [];
cache[0] = 0;
cache[1] = 1;
function memoize(number){
if(cache[number] !== undefined){
return cache[number];
}
cache[number] = memoize(number-1) + memoize(number-2);
return cache[number];
}
const result = memoize(n);
return result;
};
解法2:bottom-up
var fib = function(n) {
if(n <= 1){
return n;
}
const cache = [];
cache[0] = 0;
cache[1] = 1;
for(let i=2;i<=n;i++){
cache[i] = cache[i-1] + cache[i-2];
}
return cache[n];
};
解法3:bottom-up(空间复杂度优化)
var fib = function(n) {
if(n <= 1){
return n;
}
let prev2 = 0;
let prev1 = 1;
let result = 0;
for(let i=2;i<=n;i++){
result = prev1 + prev2;
prev2 = prev1;
prev1 = result;
}
return result;
};
LeetCode第680题:680. 验证回文字符串 Ⅱ
代码:
var validPalindrome = function(s) {
function isPalindrome(left,right){
while(left < right){
if(s[left] !== s[right]){
return false;
}
left++;
right--;
}
return true;
}
let left = 0;
let right = s.length - 1;
while(left < right){
if(s[left] !== s[right]){
const result = isPalindrome(left+1,right) || isPalindrome(left,right-1);
return result;
}
left++;
right--;
}
return true;
};
LeetCode第695题:695. 岛屿的最大面积
代码:
var maxAreaOfIsland = function(grid) {
let result = 0;
for(let row=0;row<grid.length;row++){
for(let col=0;col<grid[0].length;col++){
if(grid[row][col] === 1){
const count = dfs(row,col);
result = Math.max(count,result);
}
}
}
function dfs(row,col){
if(row<0 || row>=grid.length || col<0 || col>=grid[0].length || grid[row][col] === 0){
return 0;
}
grid[row][col] = 0;
let count = 1;
count += dfs(row-1,col);
count += dfs(row+1,col);
count += dfs(row,col-1);
count += dfs(row,col+1);
return count;
}
return result;
};
LeetCode第704题:704. 二分查找
思路:
1.left=0,right=length-1,mid=left+(right-left)/2
2.当left<=right,
如果mid上的值等于target,返回mid,如果小于target,left=mid+1(砍掉左半边),
如果大于target,right=mid-1(砍掉右半边)
3.如果while循环结束后都没有找到target,返回-1
代码:
var search = function(nums, target) {
let left = 0, right = nums.length - 1;
while(left <= right){
mid = Math.floor(left + (right - left) / 2);
if(nums[mid] === target){
return mid;
}else if(nums[mid] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
return -1;
};
LeetCode第733题:733. 图像渲染
代码:
var floodFill = function(image, sr, sc, newColor) {
if(image[sr][sc] === newColor){
return image;
}
const oldColor = image[sr][sc];
function dfs(sr,sc){
if(sr<0 || sr>=image.length || sc<0 || sc>=image[0].length || image[sr][sc] !== oldColor){
return;
}
image[sr][sc] = newColor;
dfs(sr-1,sc);
dfs(sr+1,sc);
dfs(sr,sc-1);
dfs(sr,sc+1);
}
dfs(sr,sc);
return image;
};
LeetCode第796题:796. 旋转字符串
代码:
//在s和goal的长度相等的情况下,在s后面再连接上一个s,则所有可能的情况都包含在这个新的字符串中,只需查看这个新字符串中是否包含目标字符串
var rotateString = function(s, goal) {
if(s.length !== goal.length){
return false;
}
const str = s + s;
return str.includes(goal);
};
LeetCode第836题:836. 矩形重叠
思路:
rec1右 <= rec2左
rec1左 >= rec2右
rec1下 >= rec2上
rec1上 <= rec2下
代码:
var isRectangleOverlap = function(rec1, rec2) {
if(rec1[2]<=rec2[0] || rec1[0]>=rec2[2] || rec1[1]>=rec2[3] || rec1[3]<=rec2[1]){
return false;
}else{
return true;
}
};
LeetCode第844题:844. 比较含退格的字符串
代码:
var backspaceCompare = function(s, t) {
let i = s.length - 1, j = t.length - 1;
let backspaceS = 0, backspaceT = 0;
while(i>=0 || j>=0){
while(i>=0){
if(s[i] === '#'){
backspaceS++;
i--;
}else if(backspaceS > 0){
backspaceS--;
i--;
}else{
break;
}
}
while(j>=0){
if(t[j] === '#'){
backspaceT++;
j--;
}else if(backspaceT > 0){
backspaceT--;
j--;
}else{
break;
}
}
if(s[i] !== t[j]){
return false;
}
if((i<0 && j>=0) || (i>=0 && j<0)){
return false;
}
i--;
j--;
}
return true;
};
LeetCode第876题:876. 链表的中间结点
代码:
var middleNode = function(head) {
let slow = head;
let fast = head;
while(fast !== null && fast.next !== null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
};
LeetCode第904题:904. 水果成篮
代码:
var totalFruit = function(fruits) {
const map = new Map();
let max = 1;
let j = 0;
for(let i=0;i<fruits.length;i++){
map.set(fruits[i],i);
if(map.size > 2){
let minIndex = fruits.length - 1;
for(const [fruit,index] of map){
if(index < minIndex){
minIndex = index;
}
}
map.delete(fruits[minIndex]);
j = minIndex + 1;
}
max = Math.max(max,i-j+1);
}
return max;
};
LeetCode第905题:905. 按奇偶排序数组
代码:
var sortArrayByParity = function(nums) {
let i = 0, j = nums.length - 1;
while(i < j){
if(nums[i]%2 === 1 && nums[j]%2 === 0){
[nums[i],nums[j]] = [nums[j],nums[i]];
i++;
j--;
}
// }else if(nums[i]%2 === 0 && nums[j]%2 === 0){
// i++;
// }else if(nums[i]%2 === 0 && nums[j]%2 === 1){
// i++;
// j--;
// }else{
// j--;
// }
if(nums[i]%2 === 0){
i++;
}
if(nums[j]%2 === 1){
j--;
}
}
return nums;
};
LeetCode第922题:922. 按奇偶排序数组 II
代码:
var sortArrayByParityII = function(nums) {
let j = 1;
for(let i=0;i<nums.length;i+=2){
if(nums[i]%2 === 1){
while(nums[j]%2 === 1 && j<nums.length){
j+=2;
}
[nums[i],nums[j]] = [nums[j],nums[i]];
}
}
return nums;
};