力扣刷题算法笔记(javascript版)下
Posted 十九万里
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣刷题算法笔记(javascript版)下相关的知识,希望对你有一定的参考价值。
上篇链接:
力扣刷题算法笔记(javascript版)上
视频链接:
人人都能看得懂的Leetcode力扣刷题教程合集
在笔记上中 一共学习了31道算法题,剩下大概20到算法题在花两天的学习和总结一下
1、 打家劫舍
题目描述:
你是一个专业的小偷 计划投钱沿街的房屋,每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通防盗系统 (所以不能偷相邻的两家)
给定一个代表每个房屋能存放金额的非负整数数组 计算你在不触动警报装置下 能偷到的最高金额。
不能偷相邻的房子 动态规划 记录到当前这个房子能收到的最大数
思路就是设定一值用来保存 然后动态的去比较相加的代码
// 33 打家劫舍
var rob = function(nums){
// 数组长度为0和1 的情况
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++){
//判断一下是选择在当前房子还是保留i-1最大的数
memo[i] = Math.max(nums[i] + memo[i-2],memo[i-1])
}
return memo[nums.length -1] ;//返回最后一个数
}
// 优化 在选择偷哪一个数组的时候可以优化 不需要单独的变量
//直接是用两个需要定义的变量
var rob = function(nums){
// 数组长度为0和1 的情况
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++){
//判断一下是选择在当前房子还是保留i-1最大的数
const temp = Math.max(nums[i] + memo[i-2],memo[i-1]) ;
prev2 = prev1;
prev1 = temp;
}
return prev1;
}
2、岛屿数量
题目描述:给定一个有‘1(陆地)和0(水)组成的二维网络,
并且他是通过水平方向活垂直方向上相邻的陆地连接而成的,可以假设网格的四个边均被水包围了
分析思路:从最开始遍历访问 如果是1就记录下来 记作一个小岛,然后他周围是1的部分都是一个小岛 这里用到“沉没”就是把1全部变成0 这样接着往后面遍历 就不存在重复的问题了,最后就能数出小岛的数量
那么重点在于沉没这个过程:
把当前的点从1变为0 然后从这个点向周围(行和列)扩散 判断是否越界(越界取消)是0的话就不做操作· 判断是1就变为0 然后在这个基础上继续扩散检查 和之前的步骤一样;最后返回小岛的数量
bfs:广度优先搜索:
dfs:深度优先搜索:
代码实现:
//判断岛屿数量
var numIslands = function (grid){
let count = 0;//初始化小岛的数量为0
function dfs(row,col){
//判断越界和等于0的时候 不需要去处理
if(row<0 || row >= grid.length || col<0 || col>=grid[0].length || grid[row][col] === "0"){
return;
}
//如果不是1 则变为0 然后的调用 这里采用了递归的方法
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"){//这里使用字符串1
count++;//增加小岛数量
dfs(row,col);//sink就是沉没函数 dfs
}
}
}
return count;
}
3、反转链表
链表的题 主要是玩指针
解题思路:
一、利用栈 (先进后出)的特点 然后在一个一个取出来 会多开一个数组
二、分四部 需要三个指针 prev curr next
初始:prev指向第一个节点前的空节点 curr指向第一个节点 next也指向第一个节点
第一步 next指向curr后一个节点 也就是第二个
第二步:curr的next指向prev (就是1指向0)
第三步:prev像后挪一步 prev= curr
第四步: curr指向next
这样就完成了一次反转 循环遍历到最后 当curr为空的时候 最后返回的是prev(因为已经反转了)
//反转链表 传统写法
var reverseList = function (head){
let prev = null;
let curr = head ;
let next = null;
while(curr !== null){
next = curr.next;
curr.next = prev; //指向反转
prev = curr;
curr = next;
}
return prev;
}
//ES6中解构赋值 可以直接使用
4、存在重复元素
描述:给定一个数组 判断是否存在重复元素 如果任何值在数组中都至少出现两次 函数返回true 如果数组中每个元素都不相同 则返回false
自己的解题思路:1、遍历 放入新数组比较/2、使用两个set 3.查找每个数组中出现数字的次数
利用数据结构 set中has属性 如果所有元素都返回has的话就最后返回true 否则返回false
// 存在重复元素
var containDuplicate = 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;
}
5、存在重复元素2
描述:给定一个整数数组和一个整数k 判断数组中是否存在两个不同的索引i和j 是的nums[i]=nums[j[ 并且i和j的差的绝对值最大为k
判断在k个元素内有没有重复的元素
解题思路:使用map 遍历数组每个数 然后存到map里面 map的key设置为数组的值 value为值的索引,判断这个数在map中有没有 没有就把值和index放进去。 后续放进去重复的数字的时候 需要判断他们的索引值 相减的绝对值是否小于等于k 是的话就返回true 不是的话就继续 但是需要把当前重复的更新到后面重复这个值
代码实现:
// 存在重复元素
var containsNearDuplicate = function(nums,k){
const map = new Map();//新建一个map用来存放数组 (主要用到index值)
for(let i= 0;i<nums.length;i++){
//如果没有i或者两个重复的i值小于k 返回true
if(map.has(nums[i]) && (i-map.get(nums[i]) <=k)){
return true;
}else{//没有的话就正常插入
map.set(nums[i],i);
}
}
return false;
}
6、除自身以外数组的乘积
给定长度为n的整数数组nums,其中n>1 返回输出数组output、, 其中output【i】等于nums中除nums【i】之外其余各元素的乘积,
注:不能使用除法 且在0(n)时间复杂度内完成此题,
解题思路:如果没有注释 思路我觉得是全部乘积起来 然后除以nums[i]
i左边的相程 右边的乘积乘起来 然后在相乘 可以使用动态规划
需要两个表格 一个是从左到右的乘积 林外一个是从右到左的乘积
优化:使用一个变量 不断更新 使用5的值的时候那个变量乘于4的值
代码实现:
var productExcptSelf = 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 * numns[i];
}
for(let i = nums.length-1; i>=0;i--){
result[i] = result[i] * product;
product = product * numns[i];
}
return result;
};
7 vaild Anagram
Anagram 的意思就是 一个单词可以通过调换顺序得到另一个单词 第一个单词里面每一个词出现的次数和第二个出现的次数一样
给定两个数组 判断这两个数组哈斯不会Anagram
Palindrome:单词从前面往后面的顺序和从后面往前面的顺序一样的
解题思路:
1、判断两个数组长度是否一直 不一致返回false
2、创建一个map 用来存储每个字符出现的次数
3、对于第一个单词的每个字母 也就是S1[i] 在map里将出现次数+1。对于第二个单词的每个字母 也就是s2[i] 在map里将出现次数-1,
4、遍历完成之后 检查map里的每一个字母出现次数是否是0 如果有一个非0的字母 则返回false 否则返回true;
代码实现:
//Anagram
var isAnagram = function(s,t){
//判断两个数组是否一致
if(s.length !== t.length){
return false;
}
const map = new Map();
// 用一个for循环遍历两个数组
for(let i=0;i<s.length;i++){
if(map.has(s[i])){
// 如果map有s[i] value就需要加一
map.set(s[i],map.get(s[i])+1);
}else{
map.set(s[i],1);
}
if(map.has(t[i])){
// 如果t有t[i]的话 value需要减一
map.set(t[i],map.get(t[i]) - 1);
}else{//没有就初始化为-1
map.set(t[i],-1);
}
}
//遍历map的时候使用map.forEach返回的是一个函数 不能返回外面的函数
// 需要使用for of 需要是哟const
for (const letter of map){
//letter的第二个如果不是0 的话
if(letter[1] !== 0){
return false;
}
}
return true;
}
8、移动零
题目描述:给定一个数组nums 编写一个函数将所有的0移动到数组的末尾 同时保持非零元素的相对顺序(之前的顺序)
注:
1、必须在原数组上进行操作,不能拷贝额外的数组
2、尽量减少操作次数
解题思路:
不是一个一个换 把非零的数进行排序 剩余的位置填充0
需要两个指针:i j
先移动i i++ 如果i遇到的是0 则跳过继续下一个 如果不是0,把当前i的位置赋值给j 然后j++
当i移动到最后的时候 就把j后面的数都变成0
i移动的时候 o跳过数值交换 这样确保了相对的顺序 最后在进行0的填充 这样0就变到了最后面。
代码实现:
// 移动0
var moveZeroes = function (nums){
let j = 0;
for(let i= 0;i<nums.length; i++){
if(nums[i] !== 0){
nums[j] = nums[i];
j++; }
}
//上面循环完一遍之后把j后面的数字全部变化成0
for (i=j;i<nums.length; i++){
nums[i] = 0;
}
return nums;
}
9 奇偶链表
描述:给定一个单链表 吧所有的奇数节点和偶数节点分别排在一起 奇数和偶数值指的是index位置的奇偶数 不是数值的奇偶 奇数排到前面 偶数排到后面
请尝试用原地算法完成 空间复杂度为0(1)
解题思路: 需要两个指针 第一个在第一位(第一个奇数结点)第二个指针
1指向3 2指向4 然后循环这个过程 最后把7指向2
代码实现:
// 奇偶链表
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){
//这里为什么不能直接使用3次next呢
odd.next = odd.next.next;
odd = odd.next;
even.next =even.next.next;
even = even.next;
}
odd.next= evenHead;//把奇数尾部指向偶数头部
return head;
}
10、两个数组的交集
描述:给定两个数组 判断他们的交集
注:输出结果中每个数都是唯一的 不考虑输出数组的顺序
思路解析:
个人:使用map 两个数组往里面插入 value大于1的就是交集
不能重复 就使用set
循环判断数组里面是否有相同的数
代码实现:
// 两个数组的交集
var intersection = function(nums1,nums2){
const result = new Set();
for(nums2.includes(nums)){
result.add(num);
}
}
return Array.from(result);
// 代码优化
//数组搜索值 复杂度0(n) set Map -.has 复杂度0(1)
// 两个数组的交集
var intersection = function (nums1, nums2) {
const result = new Set();
const set = new Set();
for (num of nums2) {
if (set.has(nums)) {
result.add(num);
}
}
}
return Array.from(result);
11 甲板上的战舰
给定一个二维的甲板 请计算其中有多少艘战舰 战舰使用x表示 空位用“。”表示 需要遵守以下规则
1给你一个有效的甲板 仅有战舰或者空位组成
2、战舰只能水平或者垂直放置 也就是说只能由1xN(1行,n列)组成 或者Nx1(N行 1列)组成 其中N可以是任意大小
3、两艘战舰之间至少有一个水平或垂直的空位分隔,即没有相邻的战舰。
解题思路:
使用沉没 小岛个数问题 判断是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++){
//判断如果等于x 周围的也等于x 就继续 否则跳出循环
if(board[row][col] === "X"){
if(row>0&& board[row-1][col] === "X"){
continue;
}
if(col>0 && board[row][col-1] === "X"){
continue;
}
}
result ++;
}
}
}
12 两数相加 ii
给定两个非空链表来代表两个非负整数,数字最高位位于链表的开始位置 他们的每个节点只存储一位数 除了数字0之外不会有0开头的
这道题要注意的是链表的长度可能不一致 所以我们不能直接从头开始相加 应该从尾部相加
解题思路:
先反转 在相加 然后再反转
利用栈 现金后出的特点 使用两个栈 依次压入 全部压入之后再取出
代码实现:
// 两链表相加
var addTowNumbers = function (l1,l2){
const stack1 = [],stack2=[];
while(l1 !==null){
//判断不为1就往里面压入
stack1.push(l1.val);
l1=l1.next;
}
while(l2 !== null){
stack2.push(l2.val);
l2= l2.next;
}
let carry = 0,curr = null;
while(stack.length !== 0 || stack2.length !== 0){
let sum = 0;
if(stack1.length !== 0){
sum += stack1.pop();
}
//进位
sum += carry;
// 处理新链表中节点问题
const node = newListNode(sum % 10)以上是关于力扣刷题算法笔记(javascript版)下的主要内容,如果未能解决你的问题,请参考以下文章