Java中查詢陣列多數元素的4種方法

banq發表於2024-03-17

在本教程中,我們將探索查詢陣列中多數元素的不同方法。對於每種方法,我們將提供各自的程式碼實現以及時間和空間複雜性的分析。

讓我們瞭解一下查詢陣列中多數元素的問題。我們得到一個整數陣列,我們的目標是確定其中是否存在多數元素。

多數元素比任何其他元素出現的頻率更高,超過了在陣列中出現超過n/2次的閾值,其中n表示陣列的長度。這意味著根據出現頻率來識別在陣列中占主導地位的元素。

在深入研究每種方法之前,我們將利用提供的示例資料作為輸入:

int[] nums = {2, 3, 2, 4, 2, 5, 2};

1、使用for迴圈
查詢多數元素的一種直接方法是使用for迴圈迭代陣列。此方法涉及使用for迴圈迭代陣列並維護每個元素的出現次數。然後,我們將檢查是否有任何元素滿足多數條件,這意味著它出現在陣列的一半以上的槽中。

在此實現中,我們使用for迴圈遍歷陣列。對於陣列中的每個元素,我們初始化一個計數變數來跟蹤其出現次數。隨後,我們再次遍歷陣列來計算當前元素的出現次數。

當我們迭代陣列時,如果遇到計數大於n/2的多數元素,我們可以立即返回該元素:

int majorityThreshold = nums.length / 2;
Integer majorityElement = null;
for (int i = 0; i < nums.length; i++) {
    int count = 0;
    for (int j = 0; j < nums.length; j++) {
        if (nums[i] == nums[j]) {
            count++;
        }
    }
    if (count > majorityThreshold) {
        majorityElement = nums[i];
        <span class=<font>"hljs-keyword">break</span>;
    }
}
assertEquals(2, majorityElement);

for迴圈方法的時間複雜度為O(n^2)。這種二次時間複雜度是由於實現中使用的巢狀迴圈而產生的,其中將陣列中的每個元素與每個其他元素進行比較。另一方面,空間複雜度為O(1)。

雖然該方法易於實現並且空間開銷最小,但由於其時間複雜度為二次方,因此對於大型陣列來說並不是最有效的。

2、使用排序方法
在這種方法中,我們利用排序演算法來有效地識別陣列中的多數元素。該策略涉及按升序對陣列進行排序,這使我們能夠識別連續出現的元素。

鑑於多數元素出現的大小超過陣列大小的一半,排序後,它將佔據中間索引(如果陣列長度為奇數)或緊鄰中間元素(如果陣列長度為偶數)。因此,透過檢查排序陣列的中間元素,我們可以確定其中之一是否符合多數元素的條件。

首先,我們使用Arrays.sort()對陣列進行升序排序。這一步至關重要,因為它使我們能夠更輕鬆地識別連續出現的元素。接下來,我們迭代排序後的陣列並跟蹤中間元素的出現次數。在迴圈內部,我們還檢查計數是否大於陣列大小的一半。

如果是,則表示當前元素出現的次數超過一半,並且被識別為多數元素。然後程式碼返回該元素。讓我們使用程式碼片段來演示這個概念:

Arrays.sort(nums);
int majorityThreshold = nums.length / 2;
int count = 0;
Integer majorityElement = null;
for (int i = 0; i < nums.length; i++) {
    if (nums[i] == nums[majorityThreshold]) {
        count++;
    }
    if (count > majorityThreshold) {
        majorityElement = nums[majorityThreshold];
        <span class=<font>"hljs-keyword">break</span>;
    }
}
assertEquals(2, majorityElement);

由於排序,這種方法的時間複雜度通常為O(n log n) ,而空間複雜度為O(1),因為它使用恆定的額外空間。與for迴圈方法相比,此方法稍微更有效,但由於排序操作的時間,它可能不是非常大的陣列的最佳解決方案。

3、使用HashMap
這種方法使用HashMap來儲存陣列中每個元素的頻率。


在這種方法中,我們迭代陣列,增加 HashMap 中遇到的每個元素的計數。最後,我們迭代HashMap並檢查是否有任何元素的數量大於陣列大小的一半。如果找到多數元素,我們將其返回;否則,我們返回 -1 表示陣列中不存在多數元素。

這是一個示例實現:

Map<Integer, Integer> frequencyMap = new HashMap<>();
Integer majorityElement = null;
for (int num : nums) {
    frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1);
}
int majorityThreshold = nums.length / 2;
for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
    if (entry.getValue() > majorityThreshold) {
        majorityElement = entry.getKey();
        <span class=<font>"hljs-keyword">break</span>;
    }
}
assertEquals(2, majorityElement);

總的來說,由於其線性時間複雜度,使用HashMap是一種更有效的方法,尤其是對於較大的資料集。由於遍歷一次陣列和遍歷一次HashMap,時間複雜度為O(n)。

然而,這種方法需要額外的HashMap空間,這對於記憶體受限的環境來說可能是一個問題。在最壞的情況下,空間複雜度將為O(n),因為HashMap可能儲存陣列中的所有唯一元素。

4、使用 Boyer-Moore 投票演算法
該演算法廣泛用於使用線性時間複雜度和固定記憶體量來查詢元素序列中的多數元素。

在初始化步驟中,我們建立兩個變數:候選元素和計數。候選元素設定為陣列中的第一個元素,計數設定為 1。

接下來,在迭代步驟中,我們迴圈遍歷陣列中的剩餘元素。對於每個後續元素,如果當前元素與候選元素相同,我們就會增加計數。這意味著該元素也可能有助於成為多數。否則,我們減少計數。這抵消了之前對該候選人的投票。

如果計數達到 0,則候選元素將重置為當前元素,並且計數將設定回 1。這是因為如果先前的元素相互抵消,當前元素可能會成為多數元素的新競爭者。

迭代整個陣列後,我們透過再次迭代陣列並計算候選元素的出現次數來進行驗證。如果候選元素出現超過 n/2 次,我們將其作為多數元素返回。否則,我們返回-1。

讓我們繼續實施:

int majorityThreshold = nums.length / 2;
int candidate = nums[0];
int count = 1;
int majorityElement = -1;
for (int i = 1; i < nums.length; i++) {
    if (count == 0) {
        candidate = nums[i];
        count = 1;
    } else if (candidate == nums[i]) {
        count++;
    } else {
        count--;
    }
}
count = 0;
for (int num : nums) {
    if (num == candidate) {
        count++;
    }
}
majorityElement = count > majorityThreshold ? candidate : -1;
assertEquals(2, majorityElement);

以下是迭代步驟的細分:

Initial stage: [Candidate (2), Count (1)]
Iteration 1: [Candidate (2), Count (0), Element (3)] <font>// "3" != candidate, count--<i>
Iteration 2: [Candidate (2), Count (1), Element (2)]
// "2" == candidate, count++<i>
Iteration 3: [Candidate (2), Count (0), Element (4)]
// "4" != candidate, count--<i>
Iteration 4: [Candidate (2), Count (1), Element (2)]
// "2" == candidate, count++<i>
Iteration 5: [Candidate (2), Count (0), Element (5)]
// "5" != candidate, count--<i>
Iteration 6: [Candidate (2), Count (1), Element (2)]
// "2" == candidate, count++<i>

該演算法的時間複雜度為O(n),空間複雜度為O(1),使其成為查詢陣列中多數元素的有效解決方案。

結論
在本文中,我們探索了查詢陣列中多數元素的各種方法。

for迴圈方法提供了簡單的實現,但由於其巢狀迴圈,對於大型陣列來說效率低下。HashMap方法提供線性時間複雜度並有效處理大型陣列,但它需要額外的HashMap儲存空間。

最後,Boyer-Moore 投票演算法提供了最佳的時間和空間複雜度,並且對於大型陣列非常有效。
 

相關文章