JS算法题,LeetCode
前端JS算法题个人练习记录,含思路+详细注释,适合零基础算法小白。
文章目录
提示:以下是本篇文章正文内容,下面代码可供参考
一、LeetCode
001.两数之和
https://leetcode.cn/problems/two-sum/description/
1.创建map
2.遍历数组
3.定义差,target-nums[i]
4.如果map.has(差),返回[i, map.get(差)]
else,map.set(值,索引)
5.全不符,直接return[]
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// 新建map
const map = new Map()
// 遍历num数组
for(let i = 0; i < nums.length; i++){
// 定义 差
const complement = target - nums[i]
// 如果map里面有nums[i]对应的差
if(map.has(complement)){
// 返回 差的索引and和数组索引i
return [map.get(complement),i]
// 如果map里面没nums[i]对应的差
}else{
// 将nums[i]和数组索引i放进map中
map.set(nums[i],i)
}
}
// 如果遍历完nums数组,仍然没有,即返回一个空数组
return []
};
002.两数相加
https://leetcode.cn/problems/add-two-numbers/description/
1.新建dummy节点为空,curr指针指向dummy,设置进位carry为0
2.只要l1和l2其中有一个链表不为空,定义两数之和sum
如果l1不为空,sum+l1的值,l1进一位;l2同理
最终sum是2个链表数之和**(sum)+进位(carry)**
curr指向新建一个节点,值为sum取模10;计算进位carry,除于10再floor向下舍
指针指向下一个节点
3.最后检查carry,如果carry>0,即需要新增一个节点来放carry的值
4.返回dummy.next
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var addTwoNumbers = function(l1, l2) {
// 定义链表头,dummy为空节点
let dummy = new ListNode()
// 指针指向链表头
let curr = dummy
// 设置进位carry
let carry = 0
// 只要l1和l2其中有一个链表不为空,就一直执行
while(l1 !== null || l2 !== null){
// 两数相加之和
let sum = 0
// 如果l1不为空
if(l1 !== null) {
// sum加上l1的值
sum += l1.val
// l1要进一位
l1 = l1.next
}
// 如果l2不为空
if(l2 !== null) {
// sum加上l2的值
sum += l2.val
// l2要进一位
l2 = l2.next
}
// 最终sum是 2个链表数之和+进位
sum += carry
// curr指向新建一个节点,值为sum取模10,12 % 10 = 2
curr.next = new ListNode(sum % 10)
// 计算进位carry,除于10再floor向下舍
carry = Math.floor(sum / 10)
// 指针指向下一个节点
curr = curr.next
}
// 最后检查carry,如果carry>0,即需要新增一个节点来放carry的值
if(carry > 0){
curr.next = new ListNode(carry)
}
// 返回头节点,即dummy.next
return dummy.next
};
003.无重复字符的最长子串
https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/
1.创建set,定义双遍历指针 i 和 j 都为0
2.定义无重复字符串最大长度maxLength为0,遍历字符串
3.如果set里没有s[i],即没有重复过的字符
将s[i]添加进set,并更新maxLength的值,取maxLength和set.size的max值
4.如果set还有set,
while循环确保set不存在s[i]
删除s[j],即第一次出现的重复值,并将j++指向下一个值
再将s[i]加入set里
5.返回maxLength
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 新建set
const set = new Set()
// 双指针,滑动窗口思想,i是遍历指针,j是指向字符串s的开头
let i = 0, j = 0, maxLength = 0
// 遍历字符串
for(i = 0; i < s.length; i++){
// 如果set里没有s[i],即没有重复过的字符
if(!set.has(s[i])){
// 添加进set
set.add(s[i])
// 更新无重复字符的最长子串的值
maxLength = Math.max(maxLength, set.size)
}else{
// 只要set里还有s[i]
// 这里要用while,确保set里不存在s[i]
while(set.has(s[i])){
// 删除s[j],即第一次出现的重复值
set.delete(s[j])
// 将j指向下一个值
j++
}
// 再将s[i]加入set里
set.add(s[i])
}
}
// 返回无重复字符的最长子串的值
return maxLength
};
005.最长回文子串
https://leetcode.cn/problems/longest-palindromic-substring/
1.如果字符串长度小于2,直接返回自身
2.创建start为0,即最长回文子串的起始位置;创建最大长度maxLength为1
3.定义中心扩展算法(left,right)双指针
只要当左右指针都不越界,且左右字符都相等时
如果s内的长度(right - left + 1)> maxLength,即在s内找到了更大的回文子串
更新maxLength的值为s的长度,更新start = left
双指针变动,left-- right++
4.遍历字符串s,考虑到两种情况
i-1,i+1
i, i+1
5.返回s.substring(start, start + maxLength)
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
// 如果字符串长度小于2,即为a这种,直接返回自身即可
if(s.length < 2){
return s
}
// 设置start,即最长回文字符串的起始位置
let start = 0
// 设置最长回文字符串的长度
let maxLength = 1
// expandAroundCenter中心扩展算法
function expandAroundCenter(left, right){
// 只要当左右都不越界,且左右字符相等时
while(left >= 0 && right < s.length && s[left] === s[right]){
// 如果s内的长度大于maxLength,即在s内找到了更大的回文子串
if(right - left + 1 > maxLength){
// 更新maxLength长度
maxLength = right - left + 1
// 更新start起始位置
start = left
}
// 中心扩展
left--
right++
}
}
for(let i = 0; i < s.length; i++){
// abcba,c为中心
expandAroundCenter(i-1, i+1)
// abba,中心在b之间
expandAroundCenter(i, i+1)
}
// 返回最长回文字符串
return s.substring(start, start + maxLength)
};
015.三数之和
https://leetcode.cn/problems/3sum/description/
1.定义result数组
2.给nums数组升序排序
3.遍历nums数组,从0至length-2,三指针注意边界
i是遍历指针,i是第一个元素时或新的数与之前的数不相等时,再进行判断,即跳过相同的数(不进行判断)
start = i + 1, end = length - 1,只要start < end
如果三数相加等于0,将三数push进result数组内,start++,end–
确保结果数组里不出现重复,只要start[i]===[i-1],start++
4.三数之和小于0,找更大的值,start++
5.三数之和大于0,找更小的值,end–
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
// 返回的结果
const result = []
// 给nums数组升序排序
nums.sort(function(a, b) {
return a - b
})
// 遍历nums数组,由于三指针,注意边界
for(let i = 0; i < nums.length-2; i++){
// i为第一个元素时或新的数与之前的数不相等时,再进行判断;
// 即跳过相同的数,不进行判断
if(i === 0 || nums[i] !== nums[i-1]){
// 三指针
let start = i + 1
let end = nums.length - 1
// 只要start不等于end
while(start < end){
// 如果三数相加等于0
if(nums[i] + nums[start] + nums[end] === 0){
// 将三数放进结果数组里
result.push([nums[i],nums[start],nums[end]])
start++
end--
// 确保结果数组里不出现重复
while(start < end && nums[start] === nums[start - 1]){
start++
}
// 三数之和小于0,找更大的值
}else if (nums[i] + nums[start] + nums[end] < 0){
start++
// 三数之和大于0,找更小的值
}else if (nums[i] + nums[start] + nums[end] > 0){
end--
}
}
}
}
return result
};
019.删除链表的倒数第N个节点
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
1.创建dummy空节点,dummy指向head
2.双指针start和end都指向dummy,遍历n控制end和start的身位,i<=n
3.只要end不为空,end.next;start.next
4.删除节点start.next=start.next.next
5.return dummy.next
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
// 定义链表头,dummy为空节点
let dummy = new ListNode()
// 让dummy指向head
dummy.next = head
// 设置双指针,利用滑动窗口,定位到倒数第n个节点
let start = dummy
let end = dummy
// 让end指针与start指针间隔n+1个位置
// i<=n,即end !== null
// i<n,即end.next !== null
for(let i = 0; i <= n; i++){
end = end.next
}
// 只要end节点不为空,就让双指针继续挪动
while(end !== null){
start = start.next
end = end.next
}
// end为空时,start节点在要删除的节点的前一个位置
// 直接将指针跳过要删除的节点,指向它的next
start.next = start.next.next
// 返回头节点
return dummy.next
};
020.有效的括号
https://leetcode.cn/problems/valid-parentheses/
1.new Map存set括号,k是左括号,v是右括号
2.new Array当stack,遍历字符串s
map里有没有这个k,有的话把v push进stack;
map里没有这个k,比较s[i]和stack.pop,不一样false
3.如果map.has(s),即Map里有s[i]这个k,即左括号,将对应的右括号存入stack
4.如果Map里没有s这个k,即右括号,比较s[i]和stack.pop是不是一样。不一样return false
5.遍历完s,看stack是否为空,不为空false;
6.最后return true
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
// 定义一个map来放配对的括号
const mapping = new Map()
mapping.set('(',")")
mapping.set('[',"]")
mapping.set('{',"}")
// 定义一个数组作为栈
const stack = []
// 遍历字符串
for(let i = 0; i < s.length; i++){
// 如果Map里有s[i],即s[i]是左括号
if(mapping.has(s[i])){
// 将Map里s[i]对应的右括号push进栈
stack.push(mapping.get(s[i]))
// 如果Map里没有s[i],即s[i]是右括号
}else{
// 如果pop出栈的不是s[i]
if(stack.pop() !== s[i]){
// 不符合有效括号集定义
return false
}
}
}
// 如果栈不为空,即还有剩余的括号
if(stack.length !== 0){
// 不符合有效括号集定义
return false
}
// 排除万难,即符合定义
return true
};
021.合并两个有序链表
https://leetcode.cn/problems/merge-two-sorted-lists/description/
1.创建空链表curr,dummy指向curry
2.只要l1和l2都不为空,while每次都curr.next
3.如果l1<l2,curr指向l1且l1.next;else curr指向l2且l2.next
4.如果剩下的l1不为空,全指向l1;l2同理
5.return dummy.next
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
// 创建一个空结点curr作为新链表
let curr = new ListNode()
// 让dummy指向curr
let dummy = curr
// 只要l1和l2都不为空
while(list1 !== null && list2 !== null){
// 如果l1的值<l2
if(list1.val < list2.val){
// curr指向l1
curr.next = list1
// l1进一步
list1 = list1.next
// 如果l2的值<l1
}else{
// curr指向l2
curr.next = list2
// l2进一步
list2 = list2.next
}
// curr进一步
curr = curr.next
}
// 如果l1还有剩余
if(list1 !== null){
// curr直接指向l1
curr.next = list1
}
// 如果l2还有剩余
if(list2 !== null){
// curr直接指向l2
curr.next = list2
}
// 返回链表dummy.next
return dummy.next
};
024.两两交换链表中的节点
https://leetcode.cn/problems/swap-nodes-in-pairs/description/
1.创建空节点dummy指向head,创建遍历指针curr指向dummy
2.只要curr后还有2个节点不为空
3.定义n1和n2,分别是curr的下一个节点和下下个节点
4.curr.next; n1.next ; n2.next
5.curr指向n1,此时n1和curr已经隔着一个节点了
6.return dummy.next
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var swapPairs = function(head) {
// 创建空节点dummy
let dummy = new ListNode()
// 让dummy节点指向head
dummy.next = head
// 定义遍历指针curr
let curr = dummy
// 只要curr后还有两个节点不为空
while(curr.next !== null && curr.next.next !== null){
//定义n1,即curr的下一个节点
let n1 = curr.next
//定义n2,即curr的下下一个节点
let n2 = curr.next.next
// curr n1 n2,按照顺序找到next
curr.next = n2
n1.next = n2.next
n2.next = n1
// 将curr指向n1,此时n1和curr已经隔着1个节点了
curr = n1
}
// 返回交换后的链表头节点
return dummy.next
};
049.字母异位词分组
https://leetcode.cn/problems/group-anagrams/description/
1.检查数组是否为空,若为空返回空数组
2.创建map,k为含26个字母的数组转换的字符串,v为数组(不同的字母异位词)
characters是含26个字母的空数组[0,…,0]; ascii是字母的索引(a为97);characters[ascii]是字母出现的次数
3.遍历strs数组,定义characters
4.遍历str字母,定义每个字母的ASCII - 97,让数组内字母出现的次数+1
5.定义key,将characters用’-'join成独一无二的字符串
6.如果map里面有k,展开运算符将str追加进map.get(k);没有k,直接将str加入v
7.遍历map,将map中的v返回给一个数组,并返回该数组作为结果
/**
* @param {string[]} strs
* @return {string[][]}
*/
var groupAnagrams = function(strs) {
// 判断字符串是否为空
if(strs.length === null){
// 如果为空,返回空数组
return []
}
// 创建map,k为含26个字母的数组转换的字符串,v为数组(不同的字母异位词)
const map = new Map()
// 遍历字符串strs中的每个元素str
for(const str of strs){
// 定义含26个字母的空数组(作为map的key)
// ES6新方法,Array(数量).fill(填充内容)
const characters = Array(26).fill(0)
// 遍历每个str的字母
for(let i = 0; i < str.length; i++){
// 定义每个字母的ASCII码,并且a为97,使得从0开始,作为characters的索引
const ascii = str.charCodeAt(i) - 97
// 让数组内对应的索引的值+1,默认为全0,例如b出现一次,即characters[2] = 0+1
characters[ascii]++
}
// 将含26个字母的数组转换的字符串,且用-连接,确保独一无二
const key = characters.join('-')
// 如果map里面有k,如已有tea,又来eat
if(map.has(key)){
// 将str追加入对应k的v中
// ES6新方法,...展开运算符
map.set(key, [...map.get(key), str])
// 如果map里面没有k
}else{
// 将str加入对应k的v中
map.set(key, [str])
}
}
// 遍历map,将v返回给一个数组
const result = []
for(const arr of map){
// 将map里的v,push进result数组
// arr[0]代表k,arr[1]代表v
result.push(arr[1])
}
// 返回结果
return result
};
053.最大子数组和
https://leetcode.cn/problems/maximum-subarray/description/
1.创建新数组memo,表示最大和的子数组
2.第一个值nums[0],直接赋给memo和max
3.遍历nums数组,从1开始,用Math.max()
比较旧数组+新元素单独成一个数组的和和新元素单独成一个数组的大小
memo[i - 1] + nums[i], nums[i]
比较max和memo[i]的大小
4.返回max
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
// 创建新数组
const memo = []
// 第一个值直接赋给数组
memo[0] = nums[0]
// 让最大值为第一个数
let max = nums[0]
// 遍历nums数组,从第一个开始
for(let i = 1; i < nums.length; i++){
// 判断保留旧数组,还是让新元素单独成一个数组
// 比较旧数组+新元素单独成一个数组和新元素单独成一个数组的大小
// nums[i]新元素单独成一个数组
// memo[i]最大数组
// memo[i - 1]最大旧数组
memo[i] = Math.max(memo[i - 1] + nums[i], nums[i])
// 判断max和memo[i]谁比较大
max = Math.max(max, memo[i])
}
// 返回最大值
return max
};
054.螺旋矩阵
https://leetcode.cn/problems/spiral-matrix/description/
1.判空,返回空数组
2.定义上下左右四个边界、方向(顺时针:右下左上)、结果数组
3.只要左 <= 右 且 上 <= 下
如果方向为右,遍历向右行,左到右,i++,push值matrix[top][i],top++,方向改为下
如果方向为下,遍历向下行,上到下,i++,push值matrix[i][right],right–,方向改为左
如果方向为左,遍历向左行,右到左,i–,push值matrix[bottom][i],bottom–,方向改为上
如果方向为上,遍历向上行,下到上,i–,push值matrix[i][left],left++,方向改为右
/**
* @param {number[][]} matrix
* @return {number[]}
*/
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 = []
// 只要左<=右且上<=下
while (left <= right && top <= bottom) {
if(direction === "right"){
// 遍历向右行,left到right
for(let i = left; i <= right; i++){
// 将值push进结果数组
result.push(matrix[top][i])
}
// 走完了top一层
top++
// 改变方向,向下
direction = "down"
}else if (direction === "down"){
// 遍历向下行,top到bottom
for(let i = top; i <= bottom; i++){
// 将值push进结果数组
result.push(matrix[i][right])
}
// 走完了right一层
right--
// 改变方向,向左
direction = "left"
}else if (direction === "left"){
// 遍历向左行,right到left
for(let i = right; i >= left; i--){
// 将值push进结果数组
result.push(matrix[bottom][i])
}
// 走完了bottom一层
bottom--
// 改变方向,向上
direction = "top"
}else if (direction === "top"){
// 遍历向上行,bottom到top
for(let i = bottom; i >= top; i--){
// 将值push进结果数组
result.push(matrix[i][left])
}
// 走完了left一层
left++
// 改变方向,向右
direction = "right"
}
}
// 返回结果
return result
};
055.跳跃游戏
https://leetcode.cn/problems/jump-game/description/
Greedy贪心算法
1.定义最大跳数maxJump为 长度-1
2.从后往前遍历数组,且从倒数第二个开始至第一个
如果 索引+值 >= maxJump,是个通的点
更新最大跳数到通的点
3.如果maxJump为0,返回true
/**
* @param {number[]} nums
* @return {boolean}
*/
var canJump = function (nums) {
// Greedy贪心算法
// 定义最大跳数为长度-1
let maxJump = nums.length - 1
// 从后往前遍历数组,且从倒数第二个开始至第一个
for (let i = nums.length - 2; i >= 0; i--) {
// 如果索引+值大于等于最大跳数,是个通的点
if (i + nums[i] >= maxJump) {
// 更新最大跳数到通的点
maxJump = i
}
}
// 如果maxJump为0,返回true
return maxJump === 0
};
DP:动态规划(从下往上),递归的表是从后往前推
1.定义数组nums长度totalLength,创建记忆数组memo存放状态:0未知,1通,-1不通,最后一位肯定是通
2.从后往前遍历,从倒数第二个至第0个
最大跳数maxJump,当前位置+可以跳跃的最大值 和 最后一位 取小值
3.从前往后遍历,从下一个i+1至最大跳数maxJump
如果任何一个后面的点是通的,即前面的往后遍历发现后面的j为1,就把前面的i也改为1,break
4.如果第一个数如果为通,则顺利回到最开始,即成功
/**
* @param {number[]} nums
* @return {boolean}
*/
var canJump = function (nums) {
// Greedy贪心算法
// 定义最大跳数为长度-1
let maxJump = nums.length - 1
// 从后往前遍历数组,且从倒数第二个开始至第一个
for (let i = nums.length - 2; i >= 0; i--) {
// 如果索引+值大于等于最大跳数,是个通的点
if (i + nums[i] >= maxJump) {
// 更新最大跳数到通的点
maxJump = i
}
}
// 如果maxJump为0,返回true
return maxJump === 0
};
056.合并区间
https://leetcode.cn/problems/merge-intervals/description/
1.判空或1个元素,返回原数组
2.数组sort升序,按照元素第一位数
3.定义curr指针为第1个元素和结果数组
4.for of遍历数组,可以合并的情况,即curr的终止位置>interval的起始位置,将curr的终止位置更新取大值
不可以合并的情况,将前一个合并后的数组curr推进结果,指针指向interval继续遍历
5.如果最后一位是可以合并的情况,需要push
6.返回结果数组
/**
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function (intervals) {
// 如果长度为0或只有1个
if (intervals.length < 2) {
return intervals
}
// 按照数组第一位数排序
intervals.sort(function (a, b) {
return a[0] - b[0]
})
// 定义指针
let curr = intervals[0]
// 定义结果数组
let result = []
// 遍历intervals数组
for (let interval of intervals) {
// 可以合并的情况,即curr的终止位置>interval的起始位置
if (curr[1] >= interval[0]) {
// 将curr的终止位置更新,取大值
curr[1] = Math.max(curr[1], interval[1])
// 不可以合并的情况
}else{
// 将前一个合并后的数组push进结果
result.push(curr)
// 指针指向interval继续遍历
curr = interval
}
}
// 如果最后一位是可以合并的情况,需要push
if(curr.length !== 0){
result.push(curr)
}
// 返回结果
return result
};
062.不同路径
https://leetcode.cn/problems/unique-paths/description/
1.创建memo二维数组
2.第一列和第一行的所有格子的不同路径数都只有1条
填充第一列为1;填充第一行为1
4.遍历memo二维数组,新格子的不同路径数 = 左边格子和上边格子路径数之和
6.返回结果,注意索引要-1
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function (m, n) {
// 创建memo数组
const memo = []
// 二维memo数组
for (let i = 0; i < n; i++) {
memo.push([])
}
// 第一列和第一行的路径只有1条
// 填充第一列为1
for (let row = 0; row < n; row++) {
memo[row][0] = 1
}
// 填充第一行为1
for (let col = 0; col < m; col++) {
memo[0][col] = 1
}
// 遍历memo二维数组
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]
}
}
// 返回结果,索引要-1
return memo[n-1][m-1]
};
066.加一
https://leetcode.cn/problems/plus-one/description/
1.从右往左遍历digits数组
2.如果元素不是9,首先是最右边的个位,加1,返回数组;
元素是9,即个位是9,仅需将个位设为0,遍历到十位会加1的
3.如果是99或999这种情况,则上述遍历不会返回结果
直接返回使用展开运算符将1放至第一位
/**
* @param {number[]} digits
* @return {number[]}
*/
var plusOne = function (digits) {
// 从右往左遍历digits数组
for (let i = digits.length - 1; i >= 0; i--) {
// 如果元素不是9,首先是最右边的个位
if (digits[i] !== 9) {
// 加1
digits[i]++
// 返回数组
return digits
// 元素是9,即个位是9
}else{
// 仅需将个位置为0,遍历到十位会加1的
digits[i] = 0
}
}
// 如果是99或999这种情况,则上述遍历不会返回结果
// 直接使用展开运算符将1放至第一位
return [1, ...digits]
};
070.爬楼梯
https://leetcode.cn/problems/climbing-stairs/description/
1.创建记忆化数组memo,定义memo[1]和memo[2]
2.遍历n,memo[i] = memo[i - 2] + memo[i - 1]
3.返回memo[n]
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function (n) {
// 创建记忆化数组memo
const memo = []
// 1楼只有1种方法,1
memo[1] = 1
// 2楼有2种方法,11,2
memo[2] = 2
// 3楼有3种方法,111,12,21
// 遍历数组n
for (let i = 3; i <= n; i++) {
// 到达i的方法数是memo[i-2]和memo[i-1]的和
memo[i] = memo[i - 2] + memo[i - 1]
}
// /返回结果
return memo[n]
};
073.矩阵置零
https://leetcode.cn/problems/set-matrix-zeroes/description/
1.创建第一列、第一行是否有0标志,默认为false
2.检查第一列是否有0,若有标志设为true;行同理
3.使用第一行和第一列,来标记其余行列是否含有0
遍历matrix数组,从1开始
如果某一个元素是0,将其对应的第一行、第一列,设为0
4.利用第一行、第一列的标0情况,将matrix中的数字标0
遍历matrix数组,从1开始
如果第一行和第一列有标0,将matrix中对应行和列的元素标0
5.处理第一行、第一列
如果第一列是否有0标志为true,将第一列全设为0;行同理
6.返回数组matrix
/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/
var setZeroes = function (matrix) {
// 设置第一行、第一列标志
let firstColHasZero = false
let firstRowHasZero = false
// 检查第一列是否有0
for (let i = 0; i < matrix.length; i++) {
// 如果有0,将第一列标志设为true
if (matrix[i][0] === 0) {
firstColHasZero = true
break
}
}
// 检查第一行是否有0
for (let i = 0; i < matrix[0].length; i++) {
// // 如果有0,将第一行标志设为true
if (matrix[0][i] === 0) {
firstRowHasZero = true
break
}
}
// 使用第一行和第一列,来标记其余行列是否含有0
// 遍历matrix数组,从1开始
for (let row = 1; row < matrix.length; row++) {
for (let col = 1; col < matrix[0].length; col++) {
// 如果某一个元素是0
if (matrix[row][col] === 0) {
// 将其对应的第一行和第一列,设为0
matrix[row][0] = 0
matrix[0][col] = 0
}
}
}
// 利用第一行和第一列标0情况,将matrix中的数字标0
// 遍历matrix数组,从1开始
for (let row = 1; row < matrix.length; row++) {
for (let col = 1; col < matrix[0].length; col++) {
// 如果第一行和第一列有标0,
if (matrix[row][0] === 0 || matrix[0][col] === 0) {
// 将matrix中对应行和列标0
matrix[row][col] = 0
}
}
}
// 处理第一行和第一列
// 如果firstColHasZero为true,将第一列全设为0
if (firstColHasZero) {
for (let i = 0; i < matrix.length; i++) {
matrix[i][0] = 0
}
}
// 如果firstRowHasZero为true,将第一行全设为0
if (firstRowHasZero) {
for (let i = 0; i < matrix[0].length; i++) {
matrix[0][i] = 0
}
}
// 返回数组matrix
return matrix
};
078.子集
https://leetcode.cn/problems/subsets/description/
1.创建结果空数组result
2.定义回溯函数backtrack,start是nums数组的开始查找的索引,curr是待处理的子集数组
把curr数组值传递push([…curr])进result
遍历nums数组,从start开始
把nums[i]push进curr
递归调用(i + 1,curr)
把curr数组pop出最后一个元素
3.backtrack(0, []),让回溯跑起来
4.返回result数组
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function (nums) {
// 定义结果数组
const result = []
// 定义回溯函数,start是nums数组的开始查找的索引,curr是待处理的子集数组
function backtrack(start, curr) {
// 将curr数组值传递给result数组
result.push([...curr])
// 遍历nums数组,从start开始
for (let i = start; i < nums.length; i++) {
// 把nums[i]加入curr数组
curr.push(nums[i])
// 递归调用
backtrack(i + 1, curr)
// 把curr数组的最后一个元素移除
curr.pop()
}
}
// 让回溯跑起来,0,[]
backtrack(0, [])
// 返回结果数组
return result
};
090.子集Ⅱ
https://leetcode.cn/problems/subsets-ii/description/
1.创建结果空数组result
2.定义回溯函数backtrack,start是nums数组的开始查找的索引,curr是待处理的子集数组
把curr数组值传递push([…curr])进result
遍历nums数组,从start开始
判断i > start且数组中的数是否与上一个相同,相同就continue跳过(i > start确保的是12和122这种情况,不误删122)
把nums[i]push进curr
递归调用(i + 1,curr)
把curr数组pop出最后一个元素
3.backtrack(0, []),让回溯跑起来
4.返回result数组
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function (nums) {
// 定义结果数组
const result = []
// 对nums数组升序排序
nums.sort((a, b) => {
return a - b
})
// 定义回溯函数,start是开始索引,curr是待处理的数组
function backtrack(start, curr) {
// 将curr数组值传递给result数组
result.push([...curr])
// 遍历nums数组,从start开始
for (let i = start; i < nums.length; i++) {
// 判断i > start且数组中的数是否与上一个相同,相同就跳过
// i>start确保的是12和122这种情况,不误删122
if (i > start && nums[i] === nums[i - 1]) {
continue
}
// 把nums[i]加入curr数组
curr.push(nums[i])
// 递归调用
backtrack(i + 1, curr)
// 把curr数组的最后一个元素移除
curr.pop()
}
}
// 让回溯跑起来,0,[]
backtrack(0, [])
// 返回结果数组
return result
};
083.删除排序链表中的重复元素
https://leetcode.cn/problems/remove-duplicates-from-sorted-list/description/
1.创建curr遍历指针等于head
2.只要curr不为空,且curr的下一位不为空(即只剩最后一位情况)
如果curr的值等于下一位curr的值,删掉下一位节点
否则,指针正常遍历
3.返回head指针
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function (head) {
// 定义遍历指针curr
let curr = head
// 只要curr不为空,且curr的下一位不为空(即只剩最后一位情况)
while(curr !== null && curr.next !== null){
// 如果curr的值等于下一位curr的值
if(curr.val === curr.next.val){
// 删掉下一位节点
curr.next = curr.next.next
// 如果curr的值不等于下一位curr的值
}else{
// 指针正常遍历
curr = curr.next
}
}
// 返回head指针
return head
}
206.反转链表
https://leetcode.cn/problems/reverse-linked-list/description/
1.定义后一个指针prev为空,遍历指针curr为head,下一个指针next为head
2.只要curr指针不为空,(next指针是为了占住下一位)
n cn p c
或 解构赋值curr.next -> prev, prev -> curr, curr -> curr.next
3.返回prev指针
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
// 定义后一个指针prev为空
let prev = null
// 定义遍历指针curr为head
let curr = head
// 定义下一个指针next为head
let next = head
// 只要curr指针不为空
while(curr !== null){
// next指针是为了占住下一位
// n cn p c
next = curr.next
curr.next = prev
prev = curr
curr = next
// 解构赋值curr.next -> prev, prev -> curr, curr -> curr.next
// [curr.next,prev,curr] = [prev,curr,curr.next]
}
// 返回prev
return prev
};
092.反转链表Ⅱ
https://leetcode.cn/problems/reverse-linked-list-ii/description/
1.定义后一个指针prev为空,遍历指针curr为head,下一个指针next为head
2.将curr和prev移到left位,遍历从1至<left
prev = curr, curr = curr.next
3.定义prev2和curr2占住位置
4.遍历从left至=right,(next指针是为了占住下一位)
n cn p c
或 解构赋值curr.next -> prev, prev -> curr, curr -> curr.next
5.如果prev2不等于null,prev2指向prev;否则head指向prev
6.curr2指向curr
7.返回head指针
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} left
* @param {number} right
* @return {ListNode}
*/
var reverseBetween = function (head, left, right) {
// 定义后一个指针prev为空
let prev = null
// 定义遍历指针curr为head
let curr = head
// 定义下一个指针next为head
let next = head
// 将curr和prev移到left位
for (let i = 1; i < left; i++) {
prev = curr
curr = curr.next
}
// 定义prev2、curr2占住位置
let prev2 = prev
let curr2 = curr
// 从left至right
for (let i = left; i <= right; i++) {
// next指针是为了占住下一位
// n cn p c
next = curr.next
curr.next = prev
prev = curr
curr = next
// 解构赋值curr.next -> prev, prev -> curr, curr -> curr.next
// [curr.next,prev,curr] = [prev,curr,curr.next]
}
// 如果left不为1,即prev不为空
if(prev2 !== null){
prev2.next = prev
// prev为空,left为1
}else{
head = prev
}
// 让curr2指回占位的curr
curr2.next = curr
// 返回head
return head
};
092.反转链表Ⅱ
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/
1.如果数组为空,返回0
2.初始化最低价格为数组第一位;初始化最大利润为0
3.遍历prices数组
如果值比最低价小,更新最低价格
如果产生了新的最大利润(当前值-最低值),更新最大利润
4.返回最大利润
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function (prices) {
// 如果数组为空,返回0
if (prices.length === 0) {
return 0
}
// 初始化最低价格为第一位
let minPrice = prices[0]
// 初始化最大利润为0
let maxProfit = 0
// 遍历prices数组
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
}
}
// 返回maxprofit
return maxProfit
};