力扣 - 劍指 Offer 39. 數組中出現次數超過一半的數字


題目

劍指 Offer 39. 數組中出現次數超過一半的數字

思路1(排序)

  • 因為題目說一定會存在超過數組長度一半的一個數字,所以我們將數組排序后,位於length/2位置的一定是眾數

代碼

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

復雜度分析

  • 時間復雜度:\(O(NlogN)\)
  • 空間復雜度:\(O(1)\)

思路2(哈希表)

  • 遍歷一遍數組,將數組的值作為鍵,出現的次數作為值,存放到哈希表中
  • 遍歷哈希表,找到出現次數最多的那一個鍵值對就是我們要的眾數

代碼

class Solution {
    public int majorityElement(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }

        int max = 0;
        int res = 0;
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (max < entry.getValue()) {
                max = entry.getValue();
                res = entry.getKey();
            }
        }
        return res;
    }
}

復雜度分析

  • 時間復雜度:\(O(N)\)
  • 空間復雜度:\(O(N)\)

思路3(分治)

  • 將數組從中間開始不斷分成兩份,直到只剩下一個元素時候開始返回
  • 如果left和right出現的頻率一樣,直接返回
  • 計算left和right在lo~hi范圍內出現的頻率,將高頻率的返回
  • 為什么可以用這個方法?比如有[1, 2, 3, 2, 2, 2, 5, 4, 2],共有9個元素,共會被分成5份(每份一個或者兩個元素),然后從每份中再獲取一個值,共獲取5個值,又因為眾數的個數要大於數組的長度,所以,眾數一定會至少被選中一個,只要被選中一個,那么我們的countInRange函數會根據范圍幫我們計算出最多出現的元素個數即眾數

代碼

class Solution {
    public int majorityElement(int[] nums) {
        return majorityElementRec(nums, 0, nums.length-1);
    }

    public int majorityElementRec(int[] nums, int lo, int hi) {
        // 如果只有一個元素,那么直接返回
        if (lo == hi) {
            return nums[lo];
        }

        // 獲取中間值
        int mid = lo + (hi - lo) / 2;
        // 遞歸左邊和右邊,直到剩下一個元素
        int left = majorityElementRec(nums, lo, mid);
        int right = majorityElementRec(nums, mid+1, hi);

        // 相等只需要返回一個
        if (left == right) {
            return left;
        }

        // 計算left和right在lo~hi范圍中的出現的次數
        int leftCount = countInRange(nums, left, lo, hi);
        int rightCount = countInRange(nums, right, lo, hi);

        // 返回出現次數多的數
        return leftCount > rightCount ? left : right;
    }

    // 統計目標數字在指定范圍出現的次數
    public int countInRange(int[] nums, int target, int lo, int hi) {
        int count = 0;
        for (int i = lo; i <= hi; i++) {
            if (nums[i] == target) {
                count++;
            }
        }
        return count;
    }
}

復雜度分析

  • 時間復雜度:\(O(NlogN)\)
  • 空間復雜度:\(O(logN)\),遞歸過程中使用了額外的棧空間

思路4(摩爾投票法)

  • 眾數記為+1,把其他數記為-1,將它們全部加起來,和最終會大於0
  • 假設眾數記為res,票數記為votes,假設有這么一個數組:[1, 2, 3, 2, 2, 2, 5, 4, 2],使用摩爾投票法的過程如下:
    • i=0時:因為當前票數為0,所以將1賦值給res,同時票數也加一。此時res=1 votes=1
    • i=1時:2不等於1,所以票數要減1,此時res=1 votes=0
    • i=2時:因為票數為0,所以眾數res要指向當前的3,然后票數加一,此時res=3 votes=1
    • i=3時:2不等於3,所以票數要減1,此時res=3 votes=0
    • i=4時:因為票數為0,所以眾數res要指向當前的2,然后票數加一,此時res=2 votes=1
    • i=5時:由於2=2,所以票數加一,此時res=2 votes=2
    • i=6時:5不等於2,所以票數要減1,此時res=2 votes=1
    • i=7時:4不等於2,所以票數要減1,此時res=2 votes=0
    • i=8時:因為票數為0,所以眾數res要指向當前的2,然后票數加一,此時res=2 votes=1
    • 最后就直接輸出res即為眾數

代碼

class Solution {
    public int majorityElement(int[] nums) {
        // 代表結果的眾數
        int res = nums[0];
        // 統計票數
        int votes = 0;

        for (int i = 0; i < nums.length; i++) {
            // 剛開始是0票,所以把數組的第一個元素作為眾數
            // 如果以后的循環votes票數被抵消掉了為0,那么下一個元素就作為眾數
            if (votes == 0) {
                res = nums[i];
            }
            // 和當前眾數相同的,那么票數就加1
            if (res == nums[i]) {
                votes++;
            } else {
                // 如果和當前票數不同,票數就被抵消掉了一個
                votes--;
            }
        }
        
        return res;
    }
}

復雜度分析

  • 時間復雜度:\(O(N)\)
  • 空間復雜度:\(O(1)\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM