數組中出現次數超過一半的數(劍指offer 面試題39)

題目描述:

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

解題思路:

方法一:

數組中某個數字出現的次數超過了數組長度的一半,我們把它叫做衆數;如果將數組排序,衆數必然出現在數組索引爲middle  = numbers.size() >> 1的位置;所以想到對數組進行排序,排序算法中最常用的就是複雜度O(nlogn)的快速排序算法,排序算法的核心操作(partion)就是隨機選擇數組中的一個數值元素作爲分界值,將小於該數值的元素放在它的左邊,將不小於該數值的元素放在它的右邊;如果隨機選擇的這個數值元素出現的位置恰好在middle位置,那麼該數值則爲衆數(因爲它的其那面的數組元素比它小,後面的數組元素不小於它,並且它恰好在數組中間位置,它就是這個數組的中位數,也是衆數);如果隨機選擇的這個數值元素出現的位置大於middle位置,那麼衆數在該數值元素的前面,對該位置之前的元素再進行一次partion;如果隨機選擇的這個數值元素出現的位置小於middle位置,那麼衆數在該數值元素的後面,對該位置之後的元素再進行一次partion;類似於二分查找直到隨機選擇的數值元素出現的位置等於middle位置

方法二:

首先設置兩個輔助變量result保存數組中的一個元素值,初始爲數組的第一個元素,cout表示次數,初始爲1,表示result出現了一次;接下來遍歷數組,如果下一個元素與result相等,則將cout 加1,如果不等則將cout減一,當cout爲零時,將當前位置的數字元素保存到result中,並將cout設置爲1,表示該數字出現了一次;遍歷數組結束,最後一次使cout爲零時對應的元素就是衆數的數值,及此時result中的數值。

爲什麼???當遍歷數組到最後一次使cout爲零對應的元素時,前面的衆數數值元素與非衆數值元素出現了相同的次數,才能使cout爲零;如果此刻的元素不是衆數,則cout必將再次爲零,因爲該位置之後的衆數元素個數大於非衆數元素個數,所以與最後一次使cout爲零這個前提條件矛盾,所以它必然是衆數。

注意事項:

1)判斷輸入數組是否爲空,是否存在衆數;

2)方法一移動了輸入數組部分元素的位置,方法二則沒有;時間複雜度方面,兩種方法都是O(n)的,方法一的時間複雜度說明參見《算法導論》相關章節;由此可見推薦使用方法二。

 

通過代碼(c++):

方法一:

#include<iostream>
#include<random>
class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty())
            return 0;
        int middle  = numbers.size() >> 1;
        int start = 0;
        int end = numbers.size() - 1;
        int index = partion(numbers,start,end);
        if(index < 0)
            return 0;
        while(index != middle)
        {
             if(index > middle)
            {
                end = index-1;
            }
            else
            {
                start = index+1;
            }
            index = partion(numbers,start,end);
        }
        //判斷輸入是否存在超過數組長度一半的數
        int result = numbers[index];
        if(!checkMoreThanHalf(numbers,result))
            return 0;
        return result;
            
    }
    //快速排序的基礎,返回選中的基準值在數組中的位置
    int partion(vector<int>&numbers, int start, int end)
    {
        int len = numbers.size();
        if(numbers.empty() || start < 0 || end >= len)
            return -1;
        //產生start-end的隨機數
        int index = RandomInRange(start,end);
        swap(numbers[index],numbers[end]);
        int small = start - 1;
        for(index = start; index < end; ++index)
        {
            if(numbers[index] < numbers[end])
            {
                ++small;
                if(small != index)
                    swap(numbers[small],numbers[index]);
            }
        }
        ++small;
        swap(numbers[small],numbers[end]);
        return small;
    }
    //產生[start,end]的隨機數
    int RandomInRange(int start,int end)
    {
        std::mt19937 rng;//隨機數類型
        rng.seed(std::random_device()());//初始化隨機數種子
        //創建一個均勻分佈,這個均勻分佈可以等概率(隨機)生成[start,end]區間的整形數字;
        std::uniform_int_distribution<int> dist(start,end);
        return dist(rng);
    }
    //判斷輸入是否存在超過數組長度一半的數
    bool checkMoreThanHalf(vector<int>&numbers,int result)
    {
        int cout = 0;
        int len = numbers.size();
        for(int i=0; i<len; ++i)
        {
            if(numbers[i] == result)
                cout++;
        }
        if(cout * 2 > len)
            return true;
        return false;
    }
};

方法二:

#include<iostream>
#include<random>
class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty())
            return 0;
        int result = numbers[0];
        int len = numbers.size();
        int cout = 1;
        for(int i = 0; i < len-1; ++i)
        {
            if(numbers[i+1] == numbers[i])
            {
                cout++;
            }
            else 
            {
                cout--;
                if(cout == 0)
                {
                    result = numbers[i+1];
                    cout = 1;
                }
            }
        }
        //判斷輸入是否存在超過數組長度一半的數
        if(!checkMoreThanHalf(numbers,result))
            return 0;
        return result;
    }

    //判斷輸入是否存在超過數組長度一半的數
    bool checkMoreThanHalf(vector<int>&numbers,int result)
    {
        int cout = 0;
        int len = numbers.size();
        for(int i=0; i<len; ++i)
        {
            if(numbers[i] == result)
                cout++;
        }
        if(cout * 2 > len)
            return true;
        return false;
    }
};

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