集合中元素相加为给定值的算法_每天一道算法题(第三期)

1e1a4e25b4f9cb899b0558a68a85f8f9.png
如果每天做一道算法题,那是不是每天都在进步?

前言

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

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

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

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

微信公众号惊天码盗同步

每天一道算法题(第三期)​mp.weixin.qq.com
8f5483f8264eb951eff6c089856a1325.png

上期回顾:

  • 爬楼梯
  • 只出现一次的数字
  • 报数
  • 最后一个单词的长度
  • 各位相加

本期题目

1、数组形式的整数加法

对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组。例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]。

给定非负整数 X 的数组形式 A,返回整数 X+K 的数组形式。

示例 1:

输入:A = [1,2,0,0], K = 34
输出:[1,2,3,4]
解释:1200 + 34 = 1234

示例 2:

输入:A = [2,7,4], K = 181
输出:[4,5,5]
解释:274 + 181 = 455

示例 3:

输入:A = [2,1,5], K = 806
输出:[1,0,2,1]
解释:215 + 806 = 1021

示例 4:

输入:A = [9,9,9,9,9,9,9,9,9,9], K = 1
输出:[1,0,0,0,0,0,0,0,0,0,0]
解释:9999999999 + 1 = 10000000000

提示:

1 <= A.length <= 10000

0 <= A[i] <= 9

0 <= K <= 10000

如果 A.length > 1,那么 A[0] != 0

题解:

思路1:逐位相加法

逐位相加需要判断情况,我们现在有两种情况,一种是数组A的长度大于K,可以使用逐位相加法;另一种情况是数组的长度小于K,可以把数组转化成数字计算,然后再转成数组。(这个方法感觉比较笨,不是一个很好的解题方法)

执行用时:168ms;内存消耗:40.5MB;

var addToArrayForm = function(A, K) {
    let len=A.length;
    let list=[];
    let num=K;
    let count=len-1;
    if(len>=(K+'').length){//数组的长度大于K
           while(count>=0){
            num+=A[count];
            if(count==0){
                if(num>=10){
                   list.push(num%10);
                   list.push(1);
                }else{
                     list.push(num%10);
                }

            }else{
                list.push(num%10);
            }
            num=Math.floor(num /10)
            count--
        }
        list= list.reverse()
    }else{//数组的长度小于K
       list=String(Number(A.join(''))+K).split('')
    }
    return list
};

思路2:数组相加法

把数字转化为数组,进行两个数组的相加,进的情况特殊处理;

执行用时:248ms;内存消耗:41.4MB;

var addToArrayForm = function(A, K) {
    let B=String(K).split('').map(i=>+i);
    A.reverse();
    B.reverse();
    const maxLength=Math.max(A.length,B.length);
    for(var i=0; i<maxLength;i++){
        const sum=(A[i]||0)+(B[i]||0);
        if(sum<=9){
            A[i]=sum;
        }else{
            A[i]=sum-10;
            A[i+1]=A[i+1]?A[i+1]+1:1;
        }
    }
    return A.reverse()
};

2、数组拆分 |

给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。

示例 1:

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

输出: 4
解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4).

提示:

1、n 是正整数,范围在 [1, 10000].

2、数组中的元素范围在 [-10000, 10000].

题解:

思路1:取余法

排序,取偶;这个题比较简单;

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

var arrayPairSum = function(nums) {
    let list=nums.sort((a,b)=>a-b);
    let num=0;
    list.forEach((item,i)=>{
        if(i%2==0){
            num+=item
        }
    })
    return num
};

思路2:跳针法

排序,循环跳针;

执行用时:288ms;内存消耗:38.9MB;

var arrayPairSum = function(nums) {
    let list=nums.sort((a,b)=>a-b);
    let num=0;
    for(let i=0;i<nums.length;i+=2){
        num+=list[i]
    }
    return num
};

3、排列硬币

你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。

给定一个数字 n,找出可形成完整阶梯行的总行数。

n 是一个非负整数,并且在32位有符号整型的范围内。

示例 1:

n = 5

硬币可排列成以下几行:
¤
¤ ¤
¤ ¤

因为第三行不完整,所以返回2.

示例 2:

n = 8

硬币可排列成以下几行:
¤
¤ ¤
¤ ¤ ¤
¤ ¤

因为第四行不完整,所以返回3.

题解:

思路1:数学公式法

能用数学公式解决的,一定可以通过代码解决;

f483598c3ab55dcb3d116241a34faeb7.png

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

var arrangeCoins = function(n) {
    return Math.floor(Math.sqrt(2*n+0.25)-0.5);
};

思路2:递增法

像这种线性递增,规律太明显,可以用条件法或循环来解决,当累加得值大于给定的n时,返回上个行数;

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

var arrangeCoins = function(n) {
    let i = 0,sum = 0;
    while (++i) {
        sum += i;
        if (sum > n) return i - 1;
    }
}

4、买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。

注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:

输入: [7,6,4,3,1]
输出: 0

解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

题析:这道题可以简化为求数组中两点之差的问题,你也可以理解为在x轴上求间距问题;在leetcode的题解上有人提到用牛顿莱布尼茨公式来解决此类问题;最终优化后的代码如同思路1。

这道题很容易造成一个问题就是内存溢出,算法里的参数,是动态生成的随机数,他有很多种可能,就拿目前的题来说,我陷入了一个错误的区域,就是用数组来存储差值,然后取数组最大值。当参数无穷长的时候,内存就会严重不足。所以很多看似没有问题的代码,其实也不是那么完美。

但是用变量来存储数据,就不会出现以上问题;关于用变量存储是怎么解决的,思路有很多了。

题解:

思路1:比差法

用一个变量来保存差值,用另一个来保存最小值;在循环中,如果遇到当前值大于最小值,就取最大的差值;如果小于就修改最小值。这是目前主要的思路。

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

var maxProfit = function(prices) {
    let curMin = prices[0];
    let sum = 0
    for (let i=0;i<prices.length;i++) {
        if (prices[i] < curMin) {
            curMin = prices[i]
            continue;
        }
        sum = Math.max(prices[i] - curMin, sum)
    }
    return sum
}

思路2:冒泡对比法

用两个循环来执行,用数组中的每一位和他后面的数比差;然后取最大值。但是时间复杂度会很大,影响性能。

执行用时:996ms;内存消耗:35.3MB;

var maxProfit = function(prices) {
    let temp=null;
    for(let i=0; i<prices.length-1;i++){
        for(let j=i+1;j<prices.length;j++){
            const num=prices[j]-prices[i];
            if(num>temp){
                temp=num
            }
        }
    }
    return temp
}

5、字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和。

注意:

1.num1num2 的长度都小于 5100.

2.num1num2 都只包含数字 0-9.

3.num1num2 都不包含任何前导零。

4.你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

题解:

思路1:双指针法

通过两个字符串的索引,逐一递减,如遇进一存在temp中,如遇短的字符串无值的时候,用0补位;需要注意的是最后的判断条件,如果最后需要进一则特殊处理;

执行用时:104ms;内存消耗:36.8MB;

var addStrings = function(num1, num2) {
    let i=num1.length-1;
    let j=num2.length-1;
    let arr=[];
    let temp=0;
    while(i>=0||j>=0){
           const n1=num1[i]-0||0;
           const n2=num2[j]-0||0;
           const num=n1+n2+temp;
           temp=num/10>=1?1:0;
           arr.push(num%10)
           i--; j--;
           if(i<0&&j<0&&temp)arr.push(1);
          }
    return arr.reverse().join('')
};

思路2:取长补短法

找到长的哪一个字符串,把短的字符串,用0填充,然后循环相加,判断是否进一;特殊处理。

执行用时:104ms;内存消耗:36.8MB;

var addStrings = function(num1, num2) {
    let maxLen=Math.max(num1.length,num2.length);
    let minLen=Math.min(num1.length,num2.length)
    if(num1.length!=maxLen){
        num1=(Array(maxLen-minLen).fill(0).join(''))+num1
    }else{
        num2=(Array(maxLen-minLen).fill(0).join(''))+num2
    }
    let arr=Array(maxLen).fill(0);
    for(let i=maxLen-1;i>=0;i--){
        let num=Number(num1[i])+Number(num2[i]);
        arr[i]=arr[i]>0?arr[i]+num:num;
        if(i==0&&arr[i]>9){
            arr[0]=arr[i]-10
            arr.unshift(1)
        }
        if(i>0&&arr[i]>9){
            arr[i]=arr[i]-10
            arr[i-1]=1
        }
    }
    return arr.join('')
};

三期结束,希望有更多的小伙伴加入,感谢大家一路的陪伴与坚持。

关注微信公众号「惊天码盗」,输入算法,拉你进群。

790b7aa1240815695837e9dc8df459a8.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值