文章目錄
卡牌分組
歸類運算
1.題目
給定一副牌,每張牌上都寫着一個整數。
此時,你需要選定一個數字 X,使我們可以將整副牌按下述規則分成 1 組或更多組:
- 每組都有 X 張牌。
- 組內所有的牌上都寫着相同的整數。
僅當你可選的 X >= 2 時返回 true。
示例 1:
輸入:[1,2,3,4,4,3,2,1]
輸出:true
解釋:可行的分組是 [1,1],[2,2],[3,3],[4,4]
示例 2:
輸入:[1,1,1,2,2,2,3,3]
輸出:false
解釋:沒有滿足要求的分組。
示例 3:
輸入:[1]
輸出:false
解釋:沒有滿足要求的分組。
示例 4:
輸入:[1,1]
輸出:true
解釋:可行的分組是 [1,1]
示例 5:
輸入:[1,1,2,2,2,2]
輸出:true
解釋:可行的分組是 [1,1],[2,2],[2,2]
提示:
- 1 <= deck.length <= 10000
- 0 <= deck[i] < 10000
測試用例
// [1, 1, 1, 2, 2, 2, 3, 3]
// [1, 1, 1, 1, 2, 2, 2, 2, 2, 2]
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3,
// 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8]
2.思路分析
元素的個數組成一個數組,數組元素最大公約數大於1即可
3.所用到的方法
求元素的個數並組成一個數組
Array.reduce
let counts = Object.values(
deck.reduce((arr, index) => {
arr[index] = arr[index] + 1 || 1
return arr
}, {})
)
Array.forEach
let tmp = {}
deck.forEach(item => {
tmp[item] = tmp[item] ? tmp[item] + 1 : 1
})
let counts = Object.values(tmp)
求最大公約數
// 兩數求最大公約數
let gcd = (pre, cur) => {
return cur === 0 ? pre : gcd(cur, pre % cur)
}
// 多個數需要轉爲數組並搭配循環使用
arr.forEach(item => {
arr[0] = gcd(arr[0], item)
})
console.log(arr[0]) // 這就是最大公約數
4.題解及優化
我的題解
var hasGroupsSizeX = function (deck) {
// 查找每種元素的個數組成一個數組
let counts = Object.values(
deck.reduce((arr, index) => {
arr[index] = arr[index] + 1 || 1
return arr
}, {})
)
// 利用輾轉相除法來計算最大公約數
let gcd = (pre, cur) => {
return cur === 0 ? pre : gcd(cur, pre % cur)
}
// 每兩項之間求最大公約數,並把結果保存到數組第一項來與下一項進行運算
counts.forEach(item => {
counts[0] = gcd(counts[0], item)
})
// 這個數組的最小公因數存在且大於1即true
return counts[0] > 1
}
修改reduce爲forEach
var hasGroupsSizeX = function(deck) {
// 查找每種元素的個數組成一個數組
let tmp = {} // 臨時變量
deck.forEach(item => {
tmp[item] = tmp[item] ? tmp[item] + 1 : 1
})
let counts = Object.values(tmp)
// 利用輾轉相除法來計算最大公約數
let gcd = (pre, cur) => {
return cur === 0 ? pre : gcd(cur, pre % cur)
}
// 每兩項之間求最大公約數,並把結果保存到數組第一項來與下一項進行運算
counts.forEach(item => {
counts[0] = gcd(counts[0], item)
})
// 這個數組的最小公因數存在且大於1即true
return counts[0] > 1
}
課程解法
var hasGroupsSizeX = function (deck) {
// 存儲每張卡牌的總數
// 修改排序的方式修改爲直接統計每個相同字符的數量,思路不變(LeetCode測試用例)
let group = []
let tmp = {}
deck.forEach(item => {
tmp[item] = tmp[item] ? tmp[item] + 1 : 1
})
for (let v of Object.values(tmp)) {
group.push(v)
}
// 此時group已經存放的是每張牌的總數了(數組只遍歷一遍,避免了排序和正則的耗時)
// 求兩個數的最大公約數
let gcd = (a, b) => {
if (b === 0) {
return a
} else {
return gcd(b, a % b)
}
}
while (group.length > 1) {
let a = group.shift()
let b = group.shift()
let v = gcd(a, b)
if (v === 1) {
return false
} else {
group.unshift(v)
}
}
return group.length ? group[0] > 1 : false
}
其他小夥伴的解法
var hasGroupsSizeX = function(deck) {
// 最大公約數計算公式
function gcd(num1, num2) {
// 利用輾轉相除法來計算最大公約數
return num2 === 0 ? num1 : gcd(num2, num1 % num2);
}
// 相同牌出現次數Map
let timeMap = new Map();
// 遍歷牌
deck.forEach(num => {
// 統計每張牌出現的次數
timeMap.set(num, timeMap.has(num) ? timeMap.get(num) + 1 : 1);
});
// Map.protype.values()返回的是一個新的Iterator對象,所以可以使用擴展運算符(...)來構造成數組
let timeAry = [...timeMap.values()];
/*
最大公約數
因爲該數組是出現次數數組,最小值至少爲1(至少出現1次),所以默認賦值爲數組首位對公約數計算無干擾
*/
let g = timeAry[0];
// 遍歷出現次數,計算最大公約數
timeAry.forEach(time => {
// 因爲需要比較所有牌出現次數的最大公約數,故需要一箇中間值
g = gcd(g, time);
});
// 判斷是否滿足題意
return g >= 2;
}
最簡解法
var hasGroupsSizeX = function(deck) {
let t = {}
deck.forEach(i => { t[i] = t[i] ? t[i] + 1 : 1 })
let arr = Object.values(t)
let gcd = (a, b) => (b === 0 ? a : gcd(b, a % b))
return arr.every(i => (arr[0] = gcd(arr[0], i)) > 1)
}
這裏使用了Array.every來檢測數組經過求最大公約數後的每一項是否符合要求,只要有一項不符合就直接返回false,這樣節省了一部分時間