leetcode算法(日更)

一.取字符串中的最长连续不重复子串的长度

例子:“abcabcbb” 3

思路:使用滑动窗口的原理,每次有效循环中左指针不动,右指针向右滑动,一直找到最长的一段不含重复字符的子串,用一个值存储最长字符串的长度。
在这一次循环中从起始点到终点的字符一定是不重复的,每次循环结束后将该此循环出的最大长度与上次的长度进行比较取最大值,并将左指针向右加一在进行下次循环

function maxStr(s) {
        let len = s.length;
        //定义右指针,最大长度
        let r = -1,max = 0
        // 使用set存储字符串中的数据,
        let set = new Set()
        for (let i = 0; i < len; i++) {
            // 左指针的移动
            if(i!=0){
                set.delete(s.charAt(i-1))
            }
            //每次判断右指针是否越界以及set中是否含有下标为 右指针+1 的字符
            while(r+1<len&&!set.has(s.charAt(r+1))){
                // set中不含有右边界字符,将s.charAt(r+1)加入set集合中
                set.add(s.charAt(r+1))
                // 右指针向右移动
                r++
            }
            max = Math.max(max,r-i+1)            
        }
        return max
}
let s = "abcabcbb"
console.log(maxStr(s));//3   最长的为'bca'

二.将数组中的每个元素排列为一个最小的数

方法一:

先将数组中的每个number先转化为String类型,在利用数组中的sort对字符的排序规则来进行快捷排序

function quickSort(arr){
    return arr.sort((a,b)=> (''+a+b)-(''+b+a)).join('')
}

方法二:

利用二分和递归的思想

function quickSort(arr){
    if(arr.length<2) return arr
    let base = Math.floor(arr.length/2)
    //取一个基准值
    let center = arr.splice(base,1)
    //定义两个数组
    let l = [], r= []
    arr.forEach(item => {
        //数组值加基准值大,将数组值放在右边
        if(''+item+center[0]>''+center[0]+item){
            r.push(item)
         //数组值加基准值小,将数组值放在左边
        }else if(''+center[0]+item>''+item+center[0]){
            l.push(item)
        }else{
            //一样大时,放在基准数组中
            center.push(item)
        }
    });
    //递归左边的数组和右边的数组并连接起来
    return quickSort(l).concat(center).concat(quickSort(r))
}
return quickSort(nums).join('');

三.第一个只出现一次的字符

方法一:

利用js中的indexOf和lastIndexOf语法来找到该字符第一次出现和最后一次出现的位置并返回.

function firstValue(s) {
    for (let x of s) {
        if (s.indexOf(x) === s.lastIndexOf(x)) return x
    }
    return ''
}

方法二:

利用map存储每个字符出现的次数,在返回出现次数为1的那个字符

function firstValue(s) {
    let map = new Map()
    for (const value of s) {
        if(!map.has(value)){
            map.set(value,1)
        }else{
            map.set(value,map.get(value)+1)
        }
    }
    for (const value of s) {
        if(map.get(value)==1){
            return value
        }
    }
    return ' '
}

四.两数之和

利用map储存数组中每个数字的值和下标

var twoSum = function(nums, target) {
    let map = new Map()
    for(let i = 0;i<nums.length;i++){
        if(map.has(target-nums[i])){
            return [map.get(target-nums[i]),i]
        }
        map.set(nums[i],i)
    }
}

五.礼物的最大价值

矩阵中从左上方走到右下方,每次只准向下和向右走,总路径的最大值

思路:获得路径的最大值就是将到一个位置的最大值放在这个 位置上。

[[1,2,3], [[1,3,6],]

​ [4,5,6], [5,10,16],

[7,8,9]] [12,20,29]

 function maxValue(grid){
    if(grid.length ==0||grid[0].length==0) return 0
    // 格式化第一列
    for(i=1;i<grid.length;i++){
        grid[i][0]+=grid[i-1][0]
    }
     // 格式化第一行
    for(i=1;i<grid[0].length;i++){
        grid[0][i]+=grid[0][i-1]
    }

    for(let i =1;i<grid.length;i++){
        for(let j = 1 ;j<grid[0].length;j++){
            grid[i][j]+=Math.max(grid[i-1][j],grid[i][j-1])
                   
        }
    }
    return grid[grid.length-1][grid[0].length-1]
           
}

六.把数字翻译成字符串(爬楼梯问题)

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

方法一:递归

给定一个数字如2134,从第一位2开始计算,如果第一位和第二位组成的数字在[10,25]之间,向下有两条递归路径,可以将下次递归起点定为1或3,依次类推,一直到下标能取到最后一位为止,即一次转换完成

const translateNum = (num) => {
    const str = num.toString();

    const dfs = (str, pointer) => {            // 随着dfs向下,pointer右移
        // 能递归到最后才算一种方法
        if (pointer >= str.length - 1) return 1; // 指针抵达边界和超出边界都返回1
        // 从下标为0开始看,
        const temp = Number(str[pointer] + str[pointer + 1]);   // 考察该2位数
        // 该两位数如果可以转换为字符,就有两个分支
        if (temp >= 10 && temp <= 25) {
            return dfs(str, pointer + 1) + dfs(str, pointer + 2); // 2个分支的结果相加
        } else {
            // 该两位数不能转换为字符,就从下标为1继续看
            return dfs(str, pointer + 1);          // 返回1个分支的结果
        }
    }

    return dfs(str, 0);
}

方法二:动态规划

 const translateNum2 = (num) => {
   const str = num.toString();
   const n = str.length;

   const dp = new Array(n + 1);
   // 为了让第一项成立,认为设置dp[0]为1,
   //如16的dp[2]为2,dp[1]为1,要使dp[2]=dp[1]+dp[0]
   dp[0] = 1;
   dp[1] = 1;

   for (let i = 2; i < n + 1; i++) {
       const temp = Number(str[i - 2] + str[i - 1]);
       //如果temp在10-25之间,则dp[i]能由dp[i-1]直接翻译,也能由dp[i-2]翻译到
       if (temp >= 10 && temp <= 25) {
           dp[i] = dp[i - 1] + dp[i - 2];
       } else {
           dp[i] = dp[i - 1];
       }
   }

   return dp[n]; // 翻译前n个数的方法数,即翻译整个数字
 }

七.扑克牌中的顺子

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

var isStraight = function (nums) {
    let set = new Set();
    let max = 0,min = 14;
    for (let a of nums) {
        // 跳过大小王;
        if (a == 0) continue;
        max = Math.max(a, max);
        min = Math.min(a, min);
        // 若有重复,提前返回 false
        if (set.has(a)) return false;
        set.add(a);
    }
    // 最大牌 - 最小牌 < 5 则可构成顺子
    return max - min < 5;
};

八.圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

一个找规律的题,**f(n,m) = (f(n-1,m)+m)%n,**只关心最后活着的人的位置,一定是0,从0开始反推

var lastRemaining = function (n, m) {
    let ans = 0;// 最终活下来那个人的初始位置
    for (let i = 2; i <= n; i++) {
        //整体右移m个位置,再将溢出i的部分移到前面
        ans = (ans + m) % i;
    }
    return ans;
};

九.回文数

将数字取反后比较

var isPalindrome = function (x) {
   if (x < 0 || (!(x % 10) && x)) return false;
   let x2 = x, res = 0;
   while (x2) {
       res = res * 10 + x2 % 10;
       x2 = Math.floor(x2 / 10);
   }
   return res === x;
};

十.0~n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

方法一:直接遍历

var missingNumber = function(nums) {
    const n = nums.length + 1;
    for (let i = 0; i < n - 1; i++) {
        if (nums[i] !== i) {
            return i;
        }
    }
    return n - 1;
}

方法二:二分

var missingNumber = function (nums) {
    var left = 0
    var right = nums.length - 1
    while (left <= right) {
        // 根据中间值比较
        var mid = parseInt((left + right) / 2);
        if (nums[mid] == mid) left = mid + 1;//说明此时左半边无误,缺失在右半边
        if (nums[mid] > mid) right = mid - 1;//说明此时左半边已缺失,另外不会有n[m]<m的情况,那不会是缺失只会是多出
    }
    return left;
};

十一.字符串的左旋

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

方法一:原生js方法操作

var reverseLeftWords = function(s, n) {
    return s.slice(n) + s.slice(0, n);
};

方法二:双指针+旋转

上述操作可以简化为将左边的字符反转,再将右边的字符反转,最后再将整体的字符反转就得到反转后的字符

var reverseLeftWords = function(s, n) {
    function reverse(s,start,end){
        while(start<end){
            [s[start],s[end]] =[s[end],s[start]]
            start++,
            end-- 
        }
    }
    let arr = Array.from(s)
    //反转左边
    reverse(arr,0,n-1)
    //反转右边
    reverse(arr,n,s.length-1)
    //反转整体
    reverse(arr,0,s.length-1)
    return arr.join('')
};

十二.统计一个数字在排序数组中出现的次数

方法一:从两边向中间遍历

var search = function(nums, target) {
    let len = nums.length
    let left=0,right = len-1
    while(nums[left]!=target&&left<len){
        left++
    }
    while(nums[right]!=target&&right>=0){
        right--
    }
    return  right>=left?right - left + 1:0
};

方法二:二分法

const length = nums.length;
    let start = -1,
        end = -1;

    let left = 0,
        right = length - 1;
    // 找到左边界:找到第一次出现
    while (left <= right) {
        let mid = Math.floor((left + right) / 2);
        if (nums[mid] === target) {
            start = mid;
            right = mid - 1; // important
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }

    left = 0;
    right = length - 1;
    // 找到右边界:找到第2次出现
    while (left <= right) {
        let mid = Math.floor((left + right) / 2);
        if (nums[mid] === target) {
            end = mid;
            left = mid + 1; // important
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }

    return start <= end && start !== -1 ? end - start + 1 : 0;

十三.二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

思路:若root是p、q的最近公共祖先,则有三种情况

1.p和q在root的子树中,且分别在root的异侧

2.p为root,q在root的左右子树中

3.q为root,p在root的左右子树中

//由底向上回溯
var lowestCommonAncestor = function (root, p, q) {
    // 找到p节点或者找到q节点或者找到最后返回
    if (!root || root === p || root === q) return root;
    const left = lowestCommonAncestor(root.left, p, q);
    const right = lowestCommonAncestor(root.right, p, q);
    if (!left) return right; // 左子树找不到,返回右子树
    if (!right) return left; // 右子树找不到,返回左子树
    return root;
};

十四.两数之和

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

function ListNode(val, next) {
      this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
 }
var addTwoNumbers = function(l1, l2) {
    //初始化进位
    let addOne = 0
    //求和链表
    let sum = new ListNode('0')
    //让head指向sum,记录链表的开头	
    let head = sum
    while (addOne || l1 || l2) {
        let val1 = l1 !== null ? l1.val : 0
        let val2 = l2 !== null ? l2.val : 0
        let r1 = val1 + val2 + addOne
        addOne = r1 >= 10 ? 1 : 0
        //将链表指针指向求的和
        sum.next = new ListNode(r1 % 10)
        //修改求和链表sum的指针
        sum = sum.next 
        //修改l1和l2的指针
        if (l1) l1 = l1.next 
        if (l2) l2 = l2.next 
    }
    return head.next
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值