【精選】JAVA算法題(二十一)

一、醜數

題目:

/**
 * 編寫一個程序判斷給定的數是否爲醜數。
 * 醜數就是隻包含質因數 2, 3, 5 的正整數。
 *
 * 示例 1:
 * 輸入: 6
 * 輸出: true
 * 解釋: 6 = 2 × 3
 *
 * 示例 2:
 * 輸入: 8
 * 輸出: true
 * 解釋: 8 = 2 × 2 × 2
 *
 * 示例 3:
 * 輸入: 14
 * 輸出: false
 * 解釋: 14 不是醜數,因爲它包含了另外一個質因數 7。
 *
 * 說明:
 *     1 是醜數。
 *     輸入不會超過 32 位有符號整數的範圍: [−2^31,  2^31 − 1]。
 */

可以直接根據醜數的判定規則進行判斷,只包含2,3,5這三個質因數,那我就把這三個數除盡看看剩不剩其他的數就好了。

    public static boolean method1(int num) {
        if (num<1) return false;
        while (num%5==0){
            num/=5;
        }
        while (num%3==0){
            num/=3;
        }
        while (num%2==0){
            num>>=1;
        }
        return num == 1;
    }

二、消失的數

題目:

/**
 * 給定一個包含 0, 1, 2, ..., n 中 n 個數的序列,找出 0 .. n 中沒有出現在序列中的那個數。
 *
 * 示例 1:
 * 輸入: [3,0,1]
 * 輸出: 2
 *
 * 示例 2:
 * 輸入: [9,6,4,2,3,5,7,0,1]
 * 輸出: 8
 *
 * 說明:
 * 你的算法應具有線性時間複雜度。你能否僅使用額外常數空間來實現?
 */

一種簡單的解決方法,把數組排序,然後遍歷判斷找出

    public static int method1(int[] nums) {
        Arrays.sort(nums);
        int i=0;
        for (;i<nums.length;i++){
            if (nums[i]!=i){
                return i;
            }
        }
        return i+1;
    }

你還可以藉助位運算,一個數異或另一個數兩次等於它本身,一個數異或它自己等於0,0異或任何數等於任何數

    public static int method2(int[] nums) {
        int res = nums.length;
        for (int i = 0; i < nums.length; ++i){
            res ^= nums[i];
            res ^= i;
        }
        return res;
    }

三、移動零

題目:

/**
 * 給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。
 *
 * 示例:
 * 輸入: [0,1,0,3,12]
 * 輸出: [1,3,12,0,0]
 *
 * 說明:
 * 必須在原數組上操作,不能拷貝額外的數組。
 * 儘量減少操作次數。
 */

第一個想到的就是快慢指針,把快指針掃描到的非0的數字賦值到慢指針的位置,快指針走完就把從慢指針當前所在位置到數組尾全部賦值爲0。

    public static void method1(int[] nums) {
        int fast = 0, slow = 0, count = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != 0) {
                count++;
                if (fast != slow) {
                    nums[slow] = nums[fast];
                }
                slow++;
            }
        }
        for (int i = count; i < nums.length; i++) {
            nums[i] = 0;
        }
    }

換一種寫法就是當快指針掃描到的非0的數字就和慢指針爲0的數字交換,我在這裏還加了一個判斷,快慢指針位置不相等,是因爲我交換的操作沒有藉助第三個變量,因爲其中一個數已知,只需要把快指針的數賦值到慢指針上,然後慢指針的數賦值爲0就可以了。

    public static void method2(int[] nums) {
        int fast = 0, slow = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != 0) {
                if (fast != slow) {
                    nums[slow] = nums[fast];
                    nums[fast]=0;
                }
                slow++;
            }
        }
    }

四、尋找中位數

題目:

/**
 * 中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。
 *
 * 例如,
 * [2,3,4] 的中位數是 3
 * [2,3] 的中位數是 (2 + 3) / 2 = 2.5
 *
 * 設計一個支持以下兩種操作的數據結構:
 *     void addNum(int num) - 從數據流中添加一個整數到數據結構中。
 *     double findMedian() - 返回目前所有元素的中位數。
 *
 * 示例:
 * addNum(1)
 * addNum(2)
 * findMedian() -> 1.5
 * addNum(3)
 * findMedian() -> 2
 *
 * 進階:
 *     如果數據流中所有整數都在 0 到 100 範圍內,你將如何優化你的算法?
 *     如果數據流中 99% 的整數都在 0 到 100 範圍內,你將如何優化你的算法?
 */

你可以在插入時做文章把它在插入時就整理成有序數組,使用二分查找插入,然後找中位數時就分兩種情況,一種是長度爲奇數一種是長度爲偶數,也可以在尋找中位數時做文章,可以使用Arrays.sort來排序數組。

        List<Integer> list;

        public MedianFinder() {
            list = new ArrayList();
        }

        public void addNum(int num) {
            int left = 0;
            int right = list.size() - 1;
            while (left < right) {
                int mid = left + (right - left) / 2;
                int val = list.get(mid);
                if (val < num) left = mid + 1;
                else right = mid;
            }
            if (list.size() > 0 && num > list.get(list.size() - 1))
                list.add(num);
            else
                list.add(left, num);
        }

        public double findMedian() {
            int len = list.size();
            if (len % 2 == 1)
                return (double) list.get(len / 2);
            else
                return list.get(len / 2) / 2.0
                     + list.get(len / 2 - 1) / 2.0;
        }

還有一種解法就是題目中提到的進階,假設所有的數都在0-100範圍內,那你就可以定義一個長度爲101的數組,存儲所有數字出現的次數,定義一個count變量,在添加數字的時候對其加一,實現計數,這樣就實現了排序和計數,在找中位數時根據總數量進行判斷更改。假設所有的數99%出現在0-100內呢,你就要多定義兩個長度來保存小於0的數出現的次數和大於100的數出現的次數,再找中位數時要注意下標的變化。

    private int[] nums=new int[103];
    private int count=0;
    public MedianFinder() {

    }

    public void addNum(int num) {
        if (num>100){
            nums[102]=nums[102]+1;
        }else if (num<0){
            nums[0]=nums[0]+1;
        } else {
            nums[num+1]=nums[num+1]+1;
        }
        count++;
    }

    public double findMedian() {
        int sum=0;
        if (count%2==0){
            int index=count/2;
            for (int i=0;i<103;i++){
                sum+=nums[i];
                if (sum==index){
                    return (i+i-1)/2.0;
                }else if (sum>index){
                    return i-1;
                }
            }
        }else {
            int index=count/2+1;
            for (int i=0;i<103;i++){
                sum+=nums[i];
                if (sum>=index){
                    return i-1;
                }
            }
        }
        return 0;
    }

 

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