1. 程式人生 > 其它 >[LeetCode] 1157. Online Majority Element In Subarray 子陣列中佔絕大多數的元素

[LeetCode] 1157. Online Majority Element In Subarray 子陣列中佔絕大多數的元素


Design a data structure that efficiently finds themajority elementof a given subarray.

Themajority elementof a subarray is an element that occursthresholdtimes or more in the subarray.

Implementing theMajorityCheckerclass:

  • MajorityChecker(int[] arr)Initializes the instance of the class with the given arrayarr
    .
  • int query(int left, int right, int threshold)returns the element in the subarrayarr[left...right]that occurs at leastthresholdtimes, or-1if no such element exists.

Example 1:

Input
["MajorityChecker", "query", "query", "query"]
[[[1, 1, 2, 2, 1, 1]], [0, 5, 4], [0, 3, 3], [2, 3, 2]]
Output
[null, 1, -1, 2]

Explanation
MajorityChecker majorityChecker = new MajorityChecker([1, 1, 2, 2, 1, 1]);
majorityChecker.query(0, 5, 4); // return 1
majorityChecker.query(0, 3, 3); // return -1
majorityChecker.query(2, 3, 2); // return 2

Constraints:

  • 1 <= arr.length <= 2 * 104
  • 1 <= arr[i] <= 2 * 104
  • 0 <= left <= right < arr.length
  • threshold <= right - left + 1
  • 2 * threshold > right - left + 1
  • At most104calls will be made toquery.

這道題讓設計一種資料結構可以有效的找出給定範圍內子陣列的多數,這裡還給了一個閾值 threshold,只要出現次數大於等於這個閾值就算是多數。可能有些人看到這裡就說,那還不簡單麼,遍歷這個子陣列區間,累加每個數字出現的次數唄,大於 threshold 就返回唄。如果就這麼簡單的話怎麼對得起這道題 Hard 的身價,當然是不行的,得另尋更高效的解決方法。既然這裡需要統計相同數字出現的次數,有一種以空間換時間的方法就是建立每個數字和其在原陣列中出現的所有位置座標組成的陣列的對映,這樣做的好處是可以快速知道任意一個數字出現的所有位置,而且座標還是從小到大有序的,為之後的二分搜尋法提供了條件。查詢的時候遍歷所有的數字,此時有了該數字在陣列中出現的所有按順序排列的座標,可以用二分法查詢第一個不小於左邊界 left 的位置,然後再用二分法查詢第一個大於右邊界 right 的位置,若二者的差值大於等於 threshold,則說明該數字在區間 [left, right] 內至少出現了 threshold 次,返回該數字即可,否則返回 -1,參見程式碼如下:


解法一:

class MajorityChecker {
public:
    MajorityChecker(vector<int>& arr) {
        for (int i = 0; i < arr.size(); ++i) {
            idxMap[arr[i]].push_back(i);
        }
    }
    
    int query(int left, int right, int threshold) {
        for (auto &a : idxMap) {
            if (a.second.size() < threshold) continue;
            auto it1 = lower_bound(begin(a.second), end(a.second), left);
            auto it2 = upper_bound(begin(a.second), end(a.second), right);
            if (it2 - it1 >= threshold) return a.first;
        }
        return -1;
    }

private:
    unordered_map<int, vector<int>> idxMap;
};

我們可以進行一些優化,比如先驗證出現次數最多的數字,因為其更有可能會符合要求,這樣的話可以給對映對兒進行排序,按照數字出現的次數從大到小排列,查詢的方法還是不變的,參見程式碼如下:


解法二:

class MajorityChecker {
public:
    MajorityChecker(vector<int>& arr) {
        unordered_map<int, vector<int>> idxMap;
        for (int i = 0; i < arr.size(); ++i) {
            idxMap[arr[i]].push_back(i);
        }
        for (auto &a : idxMap) idxVec.push_back({a.first, a.second});
        sort(begin(idxVec), end(idxVec), [](auto &a, auto &b) {return a.second.size() > b.second.size();});
    }
    
    int query(int left, int right, int threshold) {
        for (auto &a : idxVec) {
            if (a.second.size() < threshold) continue;
            auto it1 = lower_bound(begin(a.second), end(a.second), left);
            auto it2 = upper_bound(begin(a.second), end(a.second), right);
            if (it2 - it1 >= threshold) return a.first;
        }
        return -1;
    }

private:
    vector<pair<int, vector<int>>> idxVec;
};

再來看一種優化方法,這裡不是按照次數由多到少來選,而是用一種隨機的取數方法,由於多數出現的個數是超過一半的,所以隨機抽取一個數字是多數的概率是超過 50% 的,那麼連續抽多次,不中的概率就變得非常非常小,可以忽略不計,這裡將次數設定為 10 就可以了,除了隨機選數這部分,剩餘的跟之前的就沒什麼區別了,參見程式碼如下:


解法三:

class MajorityChecker {
public:
    MajorityChecker(vector<int>& arr) {
        for (int i = 0; i < arr.size(); ++i) {
            idxMap[arr[i]].push_back(i);
        }
        nums = arr;
    }
    
    int query(int left, int right, int threshold) {
        for (int i = 0; i < 10; ++i) {
            auto a = idxMap.find(nums[left + rand() % (right - left + 1)]);
            if (a->second.size() < threshold) continue;
            auto it1 = lower_bound(begin(a->second), end(a->second), left);
            auto it2 = upper_bound(begin(a->second), end(a->second), right);
            if (it2 - it1 >= threshold) return a->first;
        }
        return -1;
    }

private:
    vector<int> nums;
    unordered_map<int, vector<int>> idxMap;
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1157


類似題目:


參考資料:

https://leetcode.com/problems/online-majority-element-in-subarray/

https://leetcode.com/problems/online-majority-element-in-subarray/discuss/356108/C%2B%2B-160-ms-frequency-%2B-binary-search

https://leetcode.com/problems/online-majority-element-in-subarray/discuss/356227/C%2B%2B-Codes-of-different-approaches-(Random-Pick-Trade-off-Segment-Tree-Bucket)


LeetCode All in One 題目講解彙總(持續更新中...)