1. 程式人生 > >【精選】JAVA入門演算法題(四)

【精選】JAVA入門演算法題(四)

把敬業變成習慣。短期來看是為了僱主,長期來看是為了自己。

1.題目:全排列問題

這種問題在演算法題中應用很多,主要思路是使用遞迴來求,求n個數的全排列就是把第一個數固定後求n-1個數的全排列,不斷遞迴到只有一個數

private static void Method1() {
        disorder(array,0,array.length);
    }

    private static void disorder(int array[],int m,int n){
        if (m==n){
            for (int i=0;i<n;i++){
                System.out.print(array[i]);
            }
            System.out.println();
            return;
        }else {
            for (int i=m;i<n;i++){
                swap(array,m,i);
                disorder(array,m+1,n);
                swap(array,m,i);
            }
        }
    }

    private static void swap(int[] array,int m,int n){
        int temp=array[m];
        array[m]=array[n];
        array[n]=temp;
    }

2.題目:紙牌三角形

A,2,3,4,5,6,7,8,9 共9張紙牌排成一個正三角形(A按1計算)。要求每個邊的和相等。
下圖就是一種排法
    A
   9 6
  4   8
 3 7 5 2
 這樣的排法可能會有很多。
 如果考慮旋轉、映象後相同的算同一種,一共有多少種不同的排法呢?
 請你計算並提交該數字。

這道題就是一個典型的全排列問題,但題目有要求說旋轉、映象算一種,那麼你就要在最後的結果除以6,因為正三角旋轉有3種情況,映象有兩種情況

static int[] first = new int[9];
    static int[] s = new int[9];
    static int sum = 0;

    private static void Method1() {
        seek(0);
        System.out.println(sum / 6);//6是因為有3種旋轉2種翻轉
    }

    private static void seek(int code) {
        if (code == 9) {
            if (s[0] + s[1] + s[3] + s[5] == s[0] + s[2] + s[4] + s[8]
                    && s[0] + s[1] + s[3] + s[5] == s[5] + s[6] + s[7] + s[8])
                sum++;
            return;
        }
        for (int i = 0; i < 9; i++) {
            if (first[i] == 0) {
                first[i] = 1;
                s[code] = i + 1;
                seek(code + 1);
                first[i] = 0;
            }
        }
    }
static int num = 0;

    private static void Method2() {
        int a[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
        f1(a, 0);
        System.out.println(num / 6);
    }

    public static void f1(int a[], int k) {
        int q, w, e;
        if (k == a.length - 1) {
            q = a[0] + a[1] + a[2] + a[3];
            w = a[3] + a[4] + a[5] + a[6];
            e = a[6] + a[7] + a[8] + a[0];
            if (q == w && w == e) num++;
        }
        for (int i = k; i < a.length; i++) {
            System.out.println(i+" "+k);
            {
                int temp = a[i];
                a[i] = a[k];
                a[k] = temp;
            }
            f1(a, k + 1);
            {
                int temp = a[i];
                a[i] = a[k];
                a[k] = temp;
            }
        }
    }

3.題目:給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的 兩個 整數。你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個陣列中同樣的元素。

示例:

給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

如果不可以組成目標數字則返回[-1,-1]

題目非常簡單,只要使用for迴圈就能搞定

private static int[] getSum(int[] nums, int target) {
        for (int i=0;i<nums.length-1;i++){
            for (int j=i+1;j<nums.length;j++){
                if (nums[i]+nums[j]==target){
                    System.out.println(i+"  "+j);
                    return new int[]{i,j};
                }
            }
        }
        return new int[]{-1,-1};
    }

但是你會覺得兩層太麻煩了,一定有辦法一遍完事。這樣用到了hashmap,這種資料資料查詢十分快,是一種用控制元件換時間的資料結構,因為如果存在兩個滿足條件的數的話,當迴圈到第二個數的時候便能打印出來,這樣複雜度便降到了最低了。

 public static int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                System.out.println(map.get(complement)+"    "+i);
                return new int[] { map.get(complement), i };
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }

4.兩數之和(二叉樹)

/**
 * 給定一個二叉搜尋樹和一個目標結果,如果 BST 中存在兩個元素且它們的和等於給定的目標結果,則返回 true。
 * 輸入:
 *     5
 *    / \
 *   3   6
 *  / \   \
 * 2   4   7
 *
 * Target = 9
 *
 * 輸出: True
 */
public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
    }

現在找尋兩個數字已經從陣列轉換成了二叉樹,但是你可以使用類似的辦法,將二叉樹遍歷新增到陣列中或者列表中,然後進行查詢

private static List<Integer> integers=new ArrayList<>();
private static boolean getSum(TreeNode root, int k) {
        ergodic(root);
        if(integers.size()==1){
            if(integers.get(0)==k) return true;
            else return false;
        }
        for (int i=0;i<integers.size()-1;i++){
            for (int j=i+1;j<integers.size();j++){
                if (integers.get(i)+integers.get(j)==k){
                    return true;
                }
            }
        }
        return false;
    }

    private static void ergodic(TreeNode root) {
        if (root.left!=null){
            ergodic(root.left);
        }
        integers.add(root.val);
        if (root.right!=null){
            ergodic(root.right);
        }
    }

5.數字反轉

/**
 * 給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
 * 示例:
 * 輸入: 123
 * 輸出: 321
 *
 * 輸入: -123
 * 輸出: -321
 *
 * 輸入: 120
 * 輸出: 21
 */

這道題有兩個點需要注意,一是要注意負數的反轉,二是要注意int型別的範圍問題,比如-2147483412

兩種解法,一種是將大數分割成兩個數進行轉換,因為Integer.parseInt()的範圍比較小,超出範圍便會出錯,另一種是藉助long型別

public static int reverse(int x) {
        if (x>=0){
            char[] chars=(x+"").toCharArray();
            for (int i=0;i<chars.length/2;i++){
                char c=chars[i];
                chars[i]=chars[chars.length-i-1];
                chars[chars.length-i-1]=c;
            }
            String s=new String(chars);
            if (s.length()>=10){
                String a=s.substring(0,s.length()/2);
                String b=s.substring(s.length()/2);
                double result=Integer.parseInt(a)*Math.pow(10,b.length())+Integer.parseInt(b);
                int res=(int)result;
                if (res==Integer.MAX_VALUE){
                    return 0;
                }
                return res;
            }else {
                return Integer.parseInt(s);
            }
        }else {
            char[] chars=(x+"").toCharArray();
            for (int i=1;i<=chars.length/2;i++){
                char c=chars[i];
                chars[i]=chars[chars.length-i];
                chars[chars.length-i]=c;
            }
            String s=new String(chars);
            if (s.length()>=10){
                String a=s.substring(1,s.length()/2);
                String b=s.substring(s.length()/2);
                System.out.println(a+"   "+b+"    "+s.length()/2);
                double result=Integer.parseInt(a)*Math.pow(10,b.length())+Integer.parseInt(b);
                System.out.println(result);
                int res=(int)-result;
                if (res==Integer.MIN_VALUE){
                    return 0;
                }
                return res;
            }else {
                return Integer.parseInt(s);
            }
        }
public static int reverse(int x) {
             long z = x;
             String str = String.valueOf(Math.abs(z));
             StringBuilder conStr = new StringBuilder();
             int len = str.length();
             while( len > 0){
                 conStr.append(str.charAt(len - 1));
                 len--;
             }
             Long l = Long.parseLong(conStr.toString());
             if(l > Integer.MAX_VALUE){
                 return 0;
             }
             if(x >= 0)
                return l.intValue();
             else{
                 return -l.intValue();
             }
         }

但是你還是不滿意,你覺得完全沒必要這麼麻煩,只要迴圈取個位數然後拼接到另一個數的個位數上就可以了嘛,然後中間放一個判斷溢位的,怎麼做呢

pop = x % 10;//取出來個位數
x /= 10;//把十位數變成個位數

temp = rev * 10 + pop;//拼接到另一個數上
rev = temp;
//迴圈此操作

那怎麼判斷是否溢位呢?如果temp=rev*10+pop溢位,那麼rev>=IntMax/10,然後我們分情況進行處理

如果rev>IntMax/10,temp=rev*10+pop一定溢位

如果rev=IntMax/10,pop大於7才溢位

把邏輯寫成程式

    public int reverse(int x) {
        int rev = 0;
        while (x != 0) {
            int pop = x % 10;
            x /= 10;
            if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
            if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
            rev = rev * 10 + pop;
        }
        return rev;
    }

漂亮!