JAVA入門算法題(九)

交會修理你的朋友。這種朋友正是你人生的導師。

1.加一

/**
     *給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一。
     * 最高位數字存放在數組的首位, 數組中每個元素只存儲一個數字。
     * 你可以假設除了整數 0 之外,這個整數不會以零開頭。
     *
     * 示例 1:
     * 輸入: [1,2,3]
     * 輸出: [1,2,4]
     * 解釋: 輸入數組表示數字 123。
     *
     * 示例 2:
     * 輸入: [4,3,2,1]
     * 輸出: [4,3,2,2]
     * 解釋: 輸入數組表示數字 4321。
     */

題目中說給你一個數組,該數組表示的是一個數字,現在要加一然後返回該數字對應的數組。很簡單的一道題,需要注意的就是進位問題。最簡單的思路就是先把數組從後往前相加並處理進位,將他們存入一個List,最後根據List長度來創建數組並返回。

public static int[] method1(int[] digits) {
        ArrayList<Integer> integerList=new ArrayList<>();
        int overflow=1;
        for (int i=digits.length-1;i>=0;i--){
            integerList.add((digits[i]+overflow)%10);
            if (digits[i]+overflow>9){
                overflow=1;
            }else {
                overflow=0;
            }
        }
        if (overflow==1) integerList.add(1);
        Collections.reverse(integerList);
        int[] nums=new int[integerList.size()];
        for (int i=0;i<nums.length;i++){
            nums[i]=integerList.get(i);
        }
        return nums;
    }

當然上面這種歌算法不是最快的方法,因爲涉及到了List,Java中的List是基於數組實現的可變長數組,所以效率較低,想要高效率那就是全數組,分兩種情況考慮,一種是第一位等於10,發生了進位,原數組裝不下了,必須創建新的數組,另一種就是第一位小於10,原數組可以裝下新的數值。

private static int[] method2(int[] digits){
        if(digits==null||digits.length==0) {
            return null;
        }
        int i;
        int len=digits.length;
        digits[len-1]+=1;
        for(i=len-1;i>0;i--) {
            if(digits[i]>=10) {
                digits[i]%=10;
                digits[i-1]+=1;
            }else {
                break;
            }
        }
        if(digits[0]==10) {
            int[] a=new int[len+1];
            a[0]=1;
            digits[0]=0;
            for(i=0;i<len;i++) {
                a[i+1]=digits[i];
            }
            return a;
        }
        return digits;
    }

2.二進制加法

/**
     *給定兩個二進制字符串,返回他們的和(用二進制表示)。
     * 輸入爲非空字符串且只包含數字 1 和 0。
     *
     * 示例 1:
     * 輸入: a = "11", b = "1"
     * 輸出: "100"
     *
     * 示例 2:
     * 輸入: a = "1010", b = "1011"
     * 輸出: "10101"
     */

遇到這道題我就想到了java中字符串轉整型的方法和整型轉二進制的方法,走起

public static String method1(String a, String b) {
        return Integer.toBinaryString(Integer.parseInt(a,2)+Integer.parseInt(b,2));
    }

可想而知,這種方法在數值較大時是無法運行的,整形的最大到2的31次方減一,一旦超出這個數值就會發生轉換錯誤。

我們要想一種通用的算法,可以採用這樣的方式,因爲要對應位置相加,所以可以把兩個String倒着取數運算,運算時因爲'1'對應的ascall碼爲49,‘0’對應的是48,所以兩者相減剛好得到正確的值。把每次運算的值存入StringBuffer,處理最後的進位,因爲是追加的結果,所以最後要反轉一下String。

public static String method2(String a, String b) {
        StringBuilder sb = new StringBuilder();
        int i = a.length() - 1, j = b.length() -1, carry = 0;
        while (i >= 0 || j >= 0) {
            int sum = carry;
            if (j >= 0) sum += b.charAt(j--) - '0';
            if (i >= 0) sum += a.charAt(i--) - '0';
            sb.append(sum % 2);
            carry = sum / 2;
        }
        if (carry != 0) sb.append(carry);
        return sb.reverse().toString();
    }

判斷可以用三目運算符替代,最後的進位操作可以合併到循環中去,如果每次都是把加和結果拼接到字符串前面最後的字符串反轉也可以省去

public static String method3(String a, String b) {
        String ret = "";
        int len_a = a.length() - 1;
        int len_b = b.length() - 1;
        int c = 0;
        while (len_a >= 0 || len_b >= 0 || c == 1)
        {
            c += (len_a >= 0 ? a.charAt(len_a--) - '0' : 0);
            c += (len_b >= 0 ? b.charAt(len_b--) - '0' : 0);
            ret = (char)(c % 2 + '0')+ret;
            c /= 2;
        }
        return ret;
    }

3.平方根

 /**
     * 實現 int sqrt(int x) 函數。
     * 計算並返回 x 的平方根,其中 x 是非負整數。
     * 由於返回類型是整數,結果只保留整數的部分,小數部分將被捨去。
     *
     * 示例 1:
     * 輸入: 4
     * 輸出: 2
     *
     * 示例 2:
     * 輸入: 8
     * 輸出: 2
     * 說明: 8 的平方根是 2.82842...,由於返回類型是整數,小數部分將被捨去。
     */

看到這道題,第一時間想到了Math.sqrt()方法,試了試果然可行,它的內部實現則是調用的C方法,無法查詢到具體實現,但題目中只要求求出近似值整數,這爲我們的算法實現減少了很多難度,找一個數的近似平方根的過程其實就是在一個範圍內找一個平方近似等於目標值的數,可以採用二分查找計算。不斷比較,找到了就返回,找不到就返回最後值中較小的那一個,如果最後剩一個值直接返回,剩兩個就兩數加和除以2,因爲最後兩個一定是相連的,除以2得到的結果就是較小的那個值。

public static int method2(int x) {
        if (x<0)return 0;
        if (x<2)return x;
        int low=0,high=x/2;
        while (low <= high) {
            long mid = (low + high) / 2;
            if (mid * mid > x) {
                high = (int) (mid - 1);
            } else if (mid * mid < x) {
                low = (int) (mid + 1);
            } else {
                return (int) mid;
            }
        }
        return high;
    }

4.爬樓梯

/**
     * 假設你正在爬樓梯。需要 n 階你才能到達樓頂。
     * 每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
     * 注意:給定 n 是一個正整數。
     *
     * 示例 1:
     * 輸入: 2
     * 輸出: 2
     * 解釋: 有兩種方法可以爬到樓頂。
     * 1.  1 階 + 1 階
     * 2.  2 階
     *
     * 示例 2:
     * 輸入: 3
     * 輸出: 3
     * 解釋: 有三種方法可以爬到樓頂。
     * 1.  1 階 + 1 階 + 1 階
     * 2.  1 階 + 2 階
     * 3.  2 階 + 1 階
     */

這道題你可以這麼想,第一種辦法就是全走1,還有就是走一個2,其它都是1,那就是C_{a}^{b}的形式,最多可以走n/2個2階,比如說5個臺階就是1+C_{4}^{1}+C_{3}^{2},如此迭代。計算C的時候就是C_{m}^{n}=\frac{A_{m}^{n}}{A_{n}^{n}},這裏有需要注意的一點就是經常提到了int的極限問題,分子非常容易突破int的極限,我們可以把它們存入數組,然後從後往前把分子分母相同的部分置爲1,這樣可以減少數值的大小。

public static int method1(int n) {
        int nums=1;
        int half=n/2;
        int i=1;
        while (i<=half){
            nums+=getCxy(i,n-i);
            i++;
        }
        return nums;
    }

    public static int getCxy(int x,int y){
        int[] highs=new int[x];
        int[] lows=new int[x];
        long high=1,low=1;
        int x2=x;
        while (x>=1){
            highs[x-1]=y--;
            x--;
        }
        while (x2>=1){
            lows[x2-1]=x2;
            x2--;
        }
        for (int i=highs.length-1;i>=0;i--){
            if (highs[i]==lows[i]){
                highs[i]=1;
                lows[i]=1;
            }else {
                high*=highs[i];
                low*=lows[i];
            }
        }
        return (int)(high/low);
    }

如果你仔細算過前7,8位的數字,你會發現這是一個斐波那契數列,???,想了半天沒想明白,但是代碼很好寫。誰明白可以幫忙講一下

private static int method2(int n){
        int x1=1, x2=1, c=0;
        for (int i=0;i<n-1;i++){
            c=x2;
            x2=x1+x2;
            x1=c;
        }
        return x2;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章