LeetCode.914-一副牌中的X(X of a Kind in a Deck of Cards)

這是悅樂書的第352次更新,第377篇原創

java

01 看題和準備

今天介紹的是LeetCode算法題中Easy級別的第214題(順位題號是914)。在一副牌中,每張牌上都寫有一個整數。算法

當且僅當您能夠選擇X >= 2時才返回true,以即可以將整個牌組分紅一組或多組牌,其中:數組

  • 每組都有X張牌。數據結構

  • 每組中的全部牌都具備相同的整數。測試

例如:優化

輸入:[1,2,3,4,4,3,2,1]
輸出:true
說明:可能的分區[1,1],[2,2],[3,3],[4,4]code

輸入:[1,1,1,2,2,2,3,3]
輸出:false
說明:沒有可能的分區。遞歸

輸入:[1]
輸出:false
說明:沒有可能的分區。get

輸入:[1,1]
輸出:true
說明:可能的分區[1,1]class

輸入:[1,1,2,2,2,2]
輸出:true
說明:可能的分區[1,1],[2,2],[2,2]

注意

  • 1 <= deck.length <= 10000

  • 0 <= deck[i] <10000

02 第一種解法

題目的意思是將數組deck中的元素進行分組,值相等的劃分爲同一組,每一組中的元素個數都相等,且大於等於2。咱們可使用一個HashMap,以deck中的元素爲key,以其出現次數爲value,對HashMapvalue值進行遍歷。

最理想的狀況,HashMap中只有一組key-value,但測試用例中確定不會這麼輕易讓你AC。來看幾組例子分析下規律:

{1,1,1,2,2,2,3,3} 
{{1,1,1},{2,2,2},{3,3}} --> {3,3,2}

{1,1,2,2,2,2,3,3,3,3,3,3} 
{{1,1},{2,2,2,2},{3,3,3,3,3,3}} --> {2,4,6}

第一個例子中,他們的最大公約數爲1,即分組的組數存在奇偶分佈的狀況。

第二個例子中,他們的最大公約數爲2,即分組的組數都是偶數,而且對於其中的4個2和6個3,是還能夠繼續拆分的,4個2拆成兩組2個2,6個3拆成三組2個3。

所以,咱們只須要判斷HashMap中每對數據的最大公約數是否是等於1便可。

public boolean hasGroupsSizeX(int[] deck) {
    if (deck.length < 2) {
        return false;
    }
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for (int num : deck) {
        map.put(num, map.getOrDefault(num, 0)+1);
    }
    int count = 0;
    for (Integer value : map.values()) {
        if (value < 2) {
            return false;
        }
        if (count == 0) {
            count = value;
        } else {
            if (count != value) {
                int gcd = 1;
                for (int i=1; i<= count || i<=value; i++) {
                    if (count%i == 0 && value%i == 0) {
                        gcd = i;
                    }
                }
                if (gcd == 1) {
                    return false;
                }
            }
        }
    }
    return true;
}


03 第二種解法

思路和第一種解法同樣,只是將其中部分代碼抽離了出來,而且簡化了一些if-else判斷。

public boolean hasGroupsSizeX2(int[] deck) {
    if (deck.length < 2) {
        return false;
    }
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for (int num : deck) {
        map.put(num, map.getOrDefault(num, 0)+1);
    }
    int min = 10001;
    for (Integer num : map.values()) {
        min = Math.min(min, num);
    }
    if (min == 1) {
        return false;
    }
    for (Integer num : map.values()) {
        if (getGCD(min, num) == 1) {
            return false;
        }
    }
    return true;
}

public int getGCD(int a, int b) {
    int gcd = 1;
    for (int i=1; i<= a || i<=b; i++) {
        if (a%i == 0 && b%i == 0) {
            gcd = i;
        }
    }
    return gcd;
}


04 第三種解法

由於限定了數組deck中元素值範圍,所以咱們可使用一個整型數組count來計算deck中各元素的出現次數,而後遍歷count中的元素,求出他們的最大公約數,只要有一對數的最大公約數等於1,就直接返回false。

public boolean hasGroupsSizeX3(int[] deck) {
    int[] count = new int[10001];
    for (int num : deck) {
        count[num]++;
    }
    int temp = -1;
    for (int num : count) {
        if (num > 0) {
            if (num < 2) {
                return false;
            }
            if (temp == -1) {
                temp = num;
            } else {
                if (getGCD(temp, num) == 1) {
                    return false;
                }
            }
        }
    }
    return true;
}

public int getGCD(int a, int b) {
    int gcd = 1;
    for (int i=1; i<= a || i<=b; i++) {
        if (a%i == 0 && b%i == 0) {
            gcd = i;
        }
    }
    return gcd;
}


05 第四種解法

對第三種解法,咱們還能夠再優化下,將求最大公約數的方法獨立處理,而且用遞歸處理。

public boolean hasGroupsSizeX4(int[] deck) {
    int[] count = new int[10001];
    for (int num : deck) {
        count[num]++;
    }
    int tem = count[deck[0]];
    for (int num : count) {
        if (num > 0) {
            tem = gcd(tem, num);
        }
    }
    return tem > 1;
}

/**
 * 利用遞歸求a和b的最大公約數
 * @param a
 * @param b
 * @return
 */
public int gcd(int a, int b) {
    return a == 0 ? b : gcd(b%a, a);
}


06 小結

算法專題目前已連續日更超過六個月,算法題文章220+篇,公衆號對話框回覆【數據結構與算法】、【算法】、【數據結構】中的任一關鍵詞,獲取系列文章合集。

以上就是所有內容,若是你們有什麼好的解法思路、建議或者其餘問題,能夠下方留言交流,點贊、留言、轉發就是對我最大的回報和支持!

相關文章
相關標籤/搜索