每天一道算法题(第四期)

432 阅读9分钟
如你需要掌握三种核心的能力:编程、写作、设计??

前言

这个活动是从2019年7月中旬开始的,人数不算多,也就几个好友和好友的好友,过程中也会有人因为工作的缘故或其他原因放弃,或许未来还会有人离开。

活动的主要形式就是在leetcode刷题,每个工作日一道题,每周做总结,目前已经是第十四期,接下来我会把每期的题做一个总结,由于我是侧重javascript,所以活动中的每道题都是以js语言来实现解题方法。

活动的规则比较严谨,群里每天早上10点之前发题,晚上10点审核,审核有管理员专门审核,称为打卡,没有打卡的人,需要发红包给管理员作为每天统计费用。

活动的目的就是培养算法思维,了解常见的算法,比如分治算法、贪心算法、动态优化等等。

微信公众号惊天码盗同步每天一道算法题(第四期)​



上期回顾:

本期题目

1、数组形式的整数加法

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。所以你应该输出1。

示例 2:

输入: [1,2], [1,2,3]

输出: 2

解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。你拥有的饼干数量和尺寸都足以让所有孩子满足。所以你应该输出2.

题解:

思路1:逐位查找法

有一数组长为0,则返回为0,先排序,然后循环小孩胃口值,num用来存储返回值;ll用来存储饼干的索引(跟之前跳针法类似);当小孩的胃口值大于饼干的时候,需要在饼干中查找大于等于相应饼干尺寸,如果没找到返回最大值,如果找到则记录索引;然后进入下次循环。

执行用时:192ms;内存消耗:38.7MB;

var findContentChildren = function(g, s) {
    let {num=0,ll=0}={};
    if(!s.length||!g.length)return 0;
    let gArray=g.sort((a,b)=>a-b);
    let sArray=s.sort((a,b)=>a-b);
    for(let i=0;i<g.length;i++){
        if(sArray[ll]){
            if(gArray[i]>sArray[ll]){
                const cur=sArray.filter(a=>a>=gArray[i])[0]||null;
                if(cur){
                    ll=sArray.indexOf(cur)+1
                    num++
                }
            }else{
                ll++
                num++
            }
        }
        
    }
    return num
}

思路2:倒序递减法

饼干尺寸和胃口值都从大到小排序,循环胃口值,逐一删除饼干尺寸。(常常从小到大排序,偶尔换个思路可能更方便)

执行用时:144ms;内存消耗:37.6MB;

var findContentChildren = function(g, s) {
    let num=0;
    let gArray=g.sort((a,b)=>b-a);
    let sArray=s.sort((a,b)=>b-a);
    gArray.forEach((item,i)=>{
        if(sArray[0]>=item){
            num++;
            sArray.shift()
        }
    })
    return num
}

2、二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组.

示例 1:

输入:
    3
   / \
  9  20
    /  \
   15   7
输出: [3, 14.5, 11]
解释:第0层的平均值是 3,  第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].

注意:

1.节点值的范围在32位有符号整数范围内。

题析:

关于图形算法,是比较复杂的,尤其是树的遍历,涉及到递归的逻辑。一般简单的情况下可以用条件语句while等来实现简单的递归。这道题同样可以,无论使用递归还是while语句都可以实现。

这道题的结果是一数组,数组的每一个值是当前层的节点值除以节点数;所以我们要明确我们需要得到的东西,层数,层数表示我们数组的长度;当前层的节点值与当前层的节点数。

这道题的核心思路有两,一是层序遍历,一层一层推进只有当前层执行完才可以推进下一层;另一种是不限制,无论当前层是否执行完,都可以执行下一层。这就会涉及到图形的两个概念:广度优先搜索和深度优先搜索。

题解:

思路1:广度优先遍历法

利用广度优先搜索的方法,逐层推进。利用queue的长度来限制是否向下推进,当所在层无值时向下推进。

执行用时:108ms;内存消耗:35.7MB;

var averageOfLevels = function(root) {
    let queue = [root], result = [], arr = [], sum = 0, length = 1
    while (queue.length) {
        let node = queue.shift()
        sum += node.val
        node.left && arr.push(node.left)
        node.right && arr.push(node.right)
        if (queue.length === 0) {
            result.push(sum/length)
            queue = arr
            length = queue.length
            arr = []
            sum = 0
        }
    }
    return result
}

思路2:深度优先遍历法

通过每层的索引来确定当前的值。同层相加。

执行用时:100ms;内存消耗:38.1MB;

var averageOfLevels = function(root) {
        let ans = [];
        let levelNum = [];//每一层的节点数
        let levelSum = [];//每一层的节点总数
        
        //递归函数
        let rescurse=(node, index, levelNum, levelSum)=>{
            if (node == null) {
                return;
            }

            if (levelNum.length <= index) {
                levelNum.push(1);
                levelSum.push(node.val);
            } else {
                levelNum[index]++;
                levelSum[index]+=node.val
            }

            rescurse(node.left, index + 1, levelNum, levelSum);
            rescurse(node.right, index + 1, levelNum, levelSum);

        }
    
        rescurse(root, 0, levelNum, levelSum);
    
        for (let i = 0; i < levelNum.length; i++) {
            ans.push(levelSum[i] / levelNum[i]);
        }
    

        return ans;
}

3、整数反转

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

题解:

思路1:数组化

先转化为数组,反转后,再转化为字符串,需要逐一限制条件。

这个思路是数组思维。

执行用时:108ms;内存消耗:35.7MB;

var reverse = function(x) {
    let num=String(Math.abs(x)).split('').reverse().join('');
    if(Math.pow(2,31)>=num&&x>0){
        return Number(num)
    }
    if(0-Math.pow(2,31)<0-num&&x<0){
         return 0-Number(num)
    }
    return 0
}

思路2:分离取余法

先分离数字(123),取得余数(3)和剩余数(12),然后余数乘十(30)加剩余数的余数(2);记录每次相加后所得余数(32)与剩余数(1);依次循环,得到终数。

这个思路是利用的数学思维。

执行用时:100ms;内存消耗:35.7MB;

var reverse = function (x) {
    let re = 0;
    while (parseInt(x / 10)) {
        re = 10 * re + x - 10 * parseInt(x / 10);
        x = parseInt(x / 10);
    }
    if (re > 214748364 || re < -214748364) return 0;
    if ((re == 214748364 && x > 7) || (re == 214748364 && x < -8)) return 0;
    re = 10 * re + x;
    return re
}

思路3:字符倒序法

转化为字符串,反转字符串,然后在循环中逐一添加。需要注意的是,转的时候要绝对值化,转之后要取整。

这个思路是字符串思维。

执行用时:112ms;内存消耗:35.7MB;

var reverse = function(x) {
    Math.abs(x)>(2**31-1)?x=0:x;
    if(x == 0) return 0
    let y = Math.abs(x).toString(),len='';
    for(var i =0;i<y.length;i++){len += y[y.length-i-1]}
    return parseInt(Math.abs(len)>(2**31-1)?len=0:(x<0?-len:len));
}

4、回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true

示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。

进阶:

你能不将整数转为字符串来解决这个问题吗?

题析:

这道题本质上和上面整数反转是一个题目,所以上面的思路下面都能用到,唯一的区别就是正负数。所以下面题解不会过多的介绍思路。

题解:

思路1:分离取余法

先转化为数组,反转后,再转化为字符串,需要逐一限制条件。

执行用时:360ms;内存消耗:42.5MB;

var isPalindrome = function(x) {
     if (x < 0) return false;
        let result = 0;
        let before = x;
        while (x > 0){
            result = result*10 + x%10;
            x = parseInt(x / 10);
        }
        return before == result?true:false;
}

5、实现strStr()

实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:

输入: haystack = "hello", needle = "ll"
输出: 2

示例 2:

输入: haystack = "aaaaa", needle = "bba"
输出: -1

说明:

needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

题解:

思路1:字符串索引法

利用字符串方法一步到位。

执行用时:64ms;内存消耗:33.8MB;

var strStr = function(haystack, needle) {
    if(!needle)return 0;
    return haystack.indexOf(needle)
    
}

执行用时:76ms;内存消耗:36MB;

var strStr = function (haystack, needle) {
    if (needle === "") return 0
    for (var i = 0; i < haystack.length; i++) {
        if (haystack[i] === needle[0]) {
            if (haystack.substring(i, i + needle.length) === needle) return i;
        }
    }
    return -1
}

四期结束,希望有更多的小伙伴加入。


关注公众号回复算法,一起学习吧。