天天一道算法題(第三期)

若是天天作一道算法題,那是否是天天都在進步?

前言

這個活動是從2019年7月中旬開始的,人數不算多,也就幾個好友和好友的好友,過程當中也會有人由於工做的緣故或其餘緣由放棄,或許將來還會有人離開。javascript

活動的主要形式就是在leetcode刷題,每一個工做日一道題,每週作總結,目前已是第十三期,接下來我會把每期的題作一個總結,因爲我是側重javascript,因此活動中的每道題都是以js語言來實現解題方法。java

活動的規則比較嚴謹,羣裏天天早上10點以前發題,晚上10點審覈,審覈有管理員專門審覈,稱爲打卡,沒有打卡的人,須要發紅包給管理員做爲天天統計費用。算法

活動的目的就是培養算法思惟,瞭解常見的算法,好比分治算法、貪心算法、動態優化等等。數組

微信公衆號驚天碼盜同步。bash

上期回顧:
微信

本期題目

一、數組形式的整數加法

對於非負整數 X 而言,X 的數組形式是每位數字按從左到右的順序造成的數組。例如,若是 X = 1231,那麼其數組形式爲 [1,2,3,1]。app

給定非負整數 X 的數組形式 A,返回整數 X+K 的數組形式。性能

示例 1:優化

輸入:A = [1,2,0,0], K = 34
輸出:[1,2,3,4]
解釋:1200 + 34 = 1234複製代碼

示例 2:ui

輸入: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()
};
複製代碼


二、數組拆分 |

給定長度爲 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).複製代碼

提示:

一、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
};
複製代碼

三、排列硬幣

你總共有 n 枚硬幣,你須要將它們擺成一個階梯形狀,第 k 行就必須正好有 k 枚硬幣。

給定一個數字 n,找出可造成完整階梯行的總行數。

n 是一個非負整數,而且在32位有符號整型的範圍內。

示例 1:

n = 5

硬幣可排列成如下幾行:
¤
¤ ¤
¤ ¤

由於第三行不完整,因此返回2.複製代碼

示例 2:

n = 8

硬幣可排列成如下幾行:
¤
¤ ¤
¤ ¤ ¤
¤ ¤

由於第四行不完整,因此返回3.複製代碼

題解:

思路1:數學公式法

能用數學公式解決的,必定能夠經過代碼解決;



執行用時: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;
    }
}
複製代碼

四、買賣股票的最佳時機

給定一個數組,它的第 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
}
複製代碼

五、字符串相加

給定兩個字符串形式的非負整數 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('')
};
複製代碼

三期結束,但願有更多的小夥伴加入,感謝你們一路的陪伴與堅持。

關注微信公衆號「驚天碼盜」,輸入算法,拉你進羣。

相關文章
相關標籤/搜索