那些算頻率的算法,如今都怎麼樣了?

面試 19:輸出數組中出現次數超過一半的數字(劍指 Offer 26 題)

上一篇推文給你們留下的習題來自於《劍指 Offer》第 29 題:數組中超過一半的數字,不知道各位去思考了麼?java

面試題:數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字並輸出。好比 {1,2,3,2,2,2,1} 中 2 的次數是 4,數組長度爲 7,因此輸出 2。要求不能修改輸入的數組。面試

準備測試用例

這道題能思考到的測試用例比較簡單。算法

  1. 輸入符合條件的數組,查看打印是否知足狀況;
  2. 輸入不符合條件的數組,查看打印;
  3. 輸入只有一個元素的數組,查看打印;
  4. 輸入無效數組,查看打印;

思考程序邏輯

第二步即是咱們的思考程序邏輯了,題目要求查找出現次數超過一半的數字。比較容易想到的思路是直接對數組排序,那中間那個值就是咱們想要的值,但這樣的想法明顯排序後會對輸入的數組順序有影響,因此咱們可能須要換一種思路。數組

再看一遍題幹,咱們不難思考到,咱們是否能夠對每一個數字進行計數,最後返回計數次數最多的值。存儲次數採用 map 作映射處理。測試

public class Test19 {
    private static int moreThanHalfNums(int[] nums) {
        if (nums == null || nums.length == 0)
            throw new RuntimeException("the length of array must be large than 0");
        int len = nums.length;
        Map<Integer, Integer> map = new HashMap<>();

        for (int num : nums) {
            if (map.containsKey(num))
                map.put(num, map.get(num) + 1);
            else
                map.put(num, 1);
        }
        int times = len / 2;
        // 查找 map 中 value 最大的值
        for (Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() > times)
                return entry.getKey();
        }
        throw new RuntimeException("invalid input!");
    }

    public static void main(String[] args) {
        int[] nums1 = {1, 2, 3, 2, 2, 4, 2, 2, 5};
        System.out.println(moreThanHalfNums(nums1));
        int[] nums2 = {1};
        System.out.println(moreThanHalfNums(nums2));
        int[] nums3 = {2, 1, 2, 1, 2, 2, 3, 2, 1};
        System.out.println(moreThanHalfNums(nums3));
        int[] nums4 = {1, 2, 3, 4, 5};
        System.out.println(moreThanHalfNums(nums4));
    }
}
複製代碼

寫畢後進行測試用例的驗證,無不例外,目前都經過,因而咱們把這樣的代碼解法遞交給面試官。spa

面試官看了這樣的算法,表示他更期待的是不使用任何輔存空間的算法。因而咱們得換個角度思考。code

數組中有一個數字出現的次數超過數組長度的一半,也就是說它出現的次數比其餘全部數字出現次數的和還要多。所以咱們能夠考慮在遍歷數組的時候保存兩個值: 一個是數組中的一個數字, 一個是次數。當咱們遍歷到下一個數字的時候,若是下一個數字和咱們以前保存的數字相同,則次數加 1 ;若是下一個數字和咱們以前保存的數不一樣,則次數減 1。若是次數爲 0,咱們須要保存下一個數字,並把次數設爲 1 。因爲咱們要找的數字出現的次數比其餘全部數字出現的次數之和還要多,那麼要找的數字確定是最後一次把次數設爲 1 時對應的數字。cdn

咱們來看這樣的思路用代碼怎麼實現。排序

public class Test19 {

    private static int moreThanHalfNums(int[] nums) {
        if (nums == null || nums.length == 0)
            throw new RuntimeException("the length of array must be large than 0");
        int result = nums[0];
        int times = 1;
        int len = nums.length;
        for (int i = 1; i < len; i++) {
            if (times == 0) {
                result = nums[i];
                times = 1;
            } else if (result == nums[i])
                times++;
            else
                times--;
        }
        times = 0;
        for (int num : nums) {
            if (num == result)
                times++;
        }
        if (times > len / 2)
            return result;
        throw new RuntimeException("invalid input!");
    }

    public static void main(String[] args) {
        int[] nums1 = {1, 2, 3, 2, 2, 4, 2, 2, 5};
        System.out.println(moreThanHalfNums(nums1));
        int[] nums2 = {1};
        System.out.println(moreThanHalfNums(nums2));
        int[] nums3 = {2, 1, 2, 1, 2, 2, 3, 2, 1};
        System.out.println(moreThanHalfNums(nums3));
        int[] nums4 = {1, 2, 3, 4, 5};
        System.out.println(moreThanHalfNums(nums4));
    }
}
複製代碼

寫畢後,驗證測試用例,一樣所有經過。get

本題最後的思路,但願你們刻意去思考和記憶一下,由於也許變一下題意,這樣的想法還能夠用到。

課後習題

咱們下一次推文的題目來源於《劍指 Offer》第 31 題:計算連續子數組的最大和。

面試題:輸入一個整型數組,數組中有正數也有負數。數組中一個或多個整數造成一個子數組,求全部子數組的和的最大值,要求時間複雜度爲 O(n)。 好比輸入 {1, -2, 3, 10, -4, 7, 2, -5},能產生子數組最大和的子數組爲 {3,10,-4,7,2},最大和爲 18。


我是南塵,只作比心的公衆號,歡迎關注我。

南塵,GitHub 7k Star,各大技術 Blog 論壇常客,出身 Android,但不單單是 Android。寫點技術,也吐點情感。作不完的開源,寫不完的矯情,你就聽聽我吹逼,不會錯~

相關文章
相關標籤/搜索