力扣刷题算法笔记(javascript版)
Posted 十九万里
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣刷题算法笔记(javascript版)相关的知识,希望对你有一定的参考价值。
本文为js版本算法视频笔记 找了很多算法这个up主讲的真的非常好 适合小白去听 算法都是力扣难度中等和简单的
b站视频链接
1、岛屿最大面积(求两数最大和)
解析:提供一个数组,给了一个target 要求是在数组中找到两个数的和为target
思路:
1.创建一个map
2、for循环遍历nums数组
3、用target减nums[i]以计算哪个数跟当前数字相加等于target
4、检查map里有没有这个数,有的话就放回结果,没有就把num[I]当做key。 i单做value放入map中
代码实现:
var towSum = 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(num[i],i);
}
}
return [];
};
2、两链表相加
视频链接
关键的地方处理好进位的问题。两个链表相加会生成一个新的链表
代码实现:
// 两数相加
var addTwoNumbers = function (l1,l2){
// 定义一个新的节点
let dummy = new listNode();
// curr是用来遍历列表用的
let curr = dummy;
let carry = 0; //处理进位用
while(l1 !== null || l2 !== null){
let sum = 0;
if(l1 !==null{
sum += l1.val;
l1 = l1.next;
}
if(l2 !== null){
sum +=l2.val;
l2= l2.next;
}
sum += carry; //检carry有没有进位
curr.next = new listNode( sum %10); //把得到的sum值各位作为新的list的节点
carry = Math.floor(sum/10); //如果有carry值 就暂存carry的值 carry的值是sum/10之后取整
}
if(carry>0){
curr.next = new listNode(carry);// 如果最后的carry值大于0,需要单独做一个新节点
}
return dummy.next;//最后return出去
}
3、找到无重复字符的最长子串
思路:
1、创建一个set 用来存放新的字符串 还需要知道这个字符串的长度
2、两个指针第一个指针指向字符串的开头-j,第二个随着for循环遍历字符串-i
3、如果set里面有s【i】,则从set里开始伤处s【j】,并且递增j,在检查set里是否有s【j】,如此反复直到set里没有s【i】为止
5、重复步骤3和4直到完成遍历整个字符串
·代码实现:
var lengthOfLongSubstring = function(s){
const set = new Set();
let i = 0;
let 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;
}
4 求三数之和
求数组里面找三个数相加等于0 答案里面必须是唯一的
求解思路:
1、给数组排序
2、遍历数组,从0遍历到length-2
3、如果当前的数字等于前一个数字,则跳过这个数字(去重)
4、如果数字不同,则设置start = i +1,
end =length -1,查看i start和end三个数的和比零大还是小 如果比0小 start++ 如果比0大 end–;如果等于0 则把这三个数加到结果中 继续下一次遍历
5、返回结果 不能重复
代码实现:
var threeSum = function (nums){
const result = [];
nums.sort(function(a,b){
return a -b;
})
for(let i =0;i<length-2;i++){
//去重 b重复的就不需要在判断
if(i === 0 || nums[i] !== nums[i-1]){
let start = i+1;
let end = nums.length -1;
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 ++;
}
while(start <end && nums[start] === nums[start +1]){
end --;
}
}else if(nums[i] + nums[start] + nums[end] < 0){
start ++;
}else{
end --;
}
}
}
}
return result;
}
5、最长回文字符串
思想是中心扩散的思想
遍历每一个字符 把每一个字符当做中心点像两边扩散看两边是否相等,知道不相等为止
回文字符串有两种情况:中间一个不同 两边相同如abcacba a为中心点
另外一个是abccba 两边都相等 没有中间点直接像两边扩散
解题思想:
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; //当有两个字符的时候,返回的回文字符串至少是其中一共字符 所以length应该初始化为1
//helper函数因为要被反复用到 所以这点提出来
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);
}
6、删除链表的倒数第N个节点
题目描述:给定一个链表 删除链表的倒数第n个节点 并返回链表的头节点
思路 使用双指针的思路定位到我们要删除的元素
单项链表是 走到3的时候直接指向5 不通过4了
边界问题 如果之后1个节点
代码实现:
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 = ne.next;
}
while(n2.next !== null){
n1 = n1.next;
n2 = n2.next;
}
n1.next = n1.next.next;
return dummy.next;
}
7、有效的括号
括号应该是正括号和反括号相对应的 怎么找出错误的括号
实现思路:
利用到栈的概念
1、创建一个HsapMap,把括号配对放进去,
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.lenght;i++){
//如果mapping里有key的话就要从mappings里面取得这个i push到stack里面
if(mappings.has(s[i])){
stack.push(mappings.get(s[i]));
}else{
//先判断他是否等于i 如果不等于的话返回false
if(stack.pop() !== s[i]){
return false;
}
}
}
//循环结束之后需要检查stack是否为空 不为空返回false
if(stack.length !== 0){
return false;
}
}
8、合并两个有序链表
题目描述:将两个有序链表合并为一个新的有序链表并返回 新链表是通过拼接给定的两个链表的所有节点组成的
思路总结:
两个数组 开一个新的数组 在两个数组的开头放一个i和j 找到比较小的push到新数组里面 i++或者J++ 在比较两个数 小的push到新数组直到结束;
链表中只有next操作,所以有局限性 要注意这个点所以需要使用dummy来进行空的占位
代码实现:
var mergeTwoLists = function(l1,l2){
//定义一个头部空节点 用他来生成结果 通过next串成一个链表
let curr = new listNode();
let dummy = curry;//
//对两个链表进行循环比较 需要判断两个链表不为空
while (l1 !== null && l2 !== null ){
if(li.val <l2.val){
curr.next = l1;
l1 = l1.next;//这一步相当于i++的操作 链表这样表示
}else{
curr.next = l2;
l2 = l2.next;
}
//上面的操作新链表德 指针没有动 我们需要手动移动指针.下次添加的时候就继续从后面添加
curr = curr.next
}
// 其中一共链表为空的时候 把剩余链表加到后面
if(l1 !==null){
curr.next = l1;
}
if(l2 !==null){
curr.next = l2;
}
//第二个指针 dummy占住了头部的位置 curr可以随便走(因为单项链表不能返回)后面不用他了
return dummy.next;
//curr在最开始我们使用的是空节点,所以最后返回的应该是dummy.next 也就是空节点后面的第一个节点
}
9、两两交换链表中的节点
题目描述:给定一个链表 两两交换其中相邻的节点 并返回交换后的链表
解题思路:
一共需要六步 需要三个指针 交换n1和n2需要在n1n2前面设置一个p指针
1、n1 = p.next
2、 n2 = p.next.next
3、p.next = n2 从第三步开始操作
4、n1.next = n2.next
5、 n2.next = n1
6、 p = n1
原图:
在走第二遍:因为上一步p和n1交换了 可以直接一直持续 直到结束
开头处理:第一种方法使用dummy去处理(空头) 第二种用if单独去处理
return的时候使用dummy.next
代码实现:
var swaPairs = function(head){
//创建一个dummy空节点在最开始
let dummy = new ListNode();
dummy.next = head ;
//遍历链表定义的指针 这里的current其实就是上面说的p
let current = dummy;
//保证n1和n2存在 单个节点不需要交换
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;
//最后为啥使用dummy.next前面说过
}
10、字符异位词分组
解释 两个单词 打乱顺序之后还能组成新的单词
思路:
两种方法:第一种是把数组进行排序 然后比较两个数组单词是否相同 最优解第二种是 空间换时间,使用26位数组
1、检查是否为空数组
2、简历一个长度为26的数组 起始值为0 26个0
3、遍历所欲字符串 将字母出现的频率放到数组中对应的位置上 利用ascii码
4遍历数组 按照相同字母出现频率进行分组归类(使用hashMap)
5、遍历map 将结果返回
代码实现:
var groupAnagrams = function (strs) {
//检查数组是否为空
if (strs.length === 0) {
return [];
}
const map = new Map();
//遍历字符串数组 遍历两次 遍历元素 或者元素出现多少次
for (const str of strs) {
const characters = Array(26).fill(0); //在长度为26的数组里面填充fill 值为0的数组
for (let i = 0; i < str.length; i++) {
const ascii = str.charCodeAt(i) - 97; //取到ascii码
characters[ascii]++;
}
const key = characters.jion(","); //生成key并转化成字符串
// 分类操作
if (map.has(key)) {
//把key放到新的map里面
// map.set(key,map.get(key).push(str)) //这是es5的方法
map.set(key, [...map.get(key), str]) //es6扩展方法
} else {
// 没有key的话就把key放到里面
map.set(key, [str])
}
}
//遍历map并返回结果
const result = [];
for(const arr of map){
result.push(arr[1])
}
return result;
}
11、最大子序和(动态规划)
题目描述:给定一个整数数组nums 找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
思路: 有两种 动态规划和分治法,这里采用动态规划
这题还思路还有点不是很明确后期补上
代码实现:
//最大子序和
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]);
}
for(let i= 1; i<memo.length;i++){
max = Math.max(max, memo[i]);
}
return max;
}
12、螺旋矩阵
思路:把二维数组走完 需要几个变量 记录走的方向
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";//一开始是right
let以上是关于力扣刷题算法笔记(javascript版)的主要内容,如果未能解决你的问题,请参考以下文章