Diretório de artigos
prefácio
Em estatística, a moda é um importante indicador de tendência central, que representa o valor que ocorre com mais frequência em um conjunto de dados. Além disso, em um conjunto, se um elemento aparece mais do que a soma de todos os outros elementos, então é chamado de modo absoluto do conjunto (equivalentemente, o modo absoluto tem mais ocorrências do que a metade do número total de elementos). Por exemplo, no conjunto de dados {1,2,3,3,4}, 3 é a moda porque aparece duas vezes, enquanto os demais valores aparecem apenas uma vez, mas não há moda absoluta .
Encontrar a moda pode nos ajudar a entender as principais características e distribuição dos dados e, em alguns casos, também pode ser usado como um valor representativo ou aproximado dos dados. Por exemplo,
- Nas pesquisas de opinião, podemos nos concentrar em qual candidato tem mais apoio;
- Nas análises de produtos, podemos prestar atenção em quais classificações ocupam a maioria;
- No processamento de imagem, podemos estar interessados em qual valor de cor ou escala de cinza ocorre com mais frequência.
Existem diferentes métodos e algoritmos para encontrar a maioria e, neste artigo, apresentaremos dois métodos comuns: força bruta e votação de Moore.
- O método de força bruta conta o número de ocorrências de cada elemento por travessia ou hash e, em seguida, encontra o elemento correspondente ao valor máximo. Este método é simples e intuitivo, mas requer espaço adicional para armazenar cada elemento e sua frequência, e possui alta complexidade temporal ou espacial.
- O método de votação Moore mantém um elemento candidato e um contador, atualiza o elemento candidato e o contador ao percorrer a matriz e, finalmente, verifica se o elemento candidato é a maioria. Esse método aproveita habilmente a condição de que haja um elemento que represente mais da metade (ou outros limites) na matriz, não exija espaço adicional e tenha uma complexidade de tempo baixa, mas esse algoritmo não pode encontrar o modo geral , pode encontrar o modo absoluto .
Este artigo apresentará os princípios, a implementação, a expansão e as limitações do método de violência e do método de votação de Moore, além de fornecer perguntas e respostas relacionadas ao LeetCode.
1. A Lei da Violência
O método de força bruta é um método simples e intuitivo para encontrar a maioria, sua ideia básica é percorrer cada elemento do array, contar o número de ocorrências do elemento e então encontrar o elemento correspondente ao valor máximo. Essa abordagem pode ser implementada de duas maneiras: uma com loops aninhados e outra com uma tabela hash.
1.1 Loops aninhados
O método de loops aninhados é percorrer cada elemento da matriz no loop externo, contar o número de ocorrências do elemento no loop interno e compará-lo com o valor máximo atual. Se o elemento ocorrer mais do que o máximo atual, atualize o máximo e o modo. Este método não requer espaço adicional, mas tem uma complexidade de tempo maior de O(n^2).
Aqui está um exemplo de código Java:
// 嵌套循环方法
public static int majorityElement1(int[] nums) {
// 初始化最大值和众数
int maxCount = 0;
Integer majority = null;
// 外层循环遍历数组
for (int i = 0; i < nums.length; i++) {
// 初始化当前元素出现次数为0
int count = 0;
// 内层循环统计当前元素出现次数
for (int j = 0; j < nums.length; j++) {
if (nums[j] == nums[i]) {
count++;
}
}
// 如果当前元素出现次数超过最大值,则更新最大值和众数
if (count > maxCount) {
maxCount = count;
majority = nums[i];
}
}
return majority;
}
1.2 Tabela de hash
A abordagem da tabela hash (ou dicionário) é utilizar uma estrutura de dados para armazenar cada elemento e sua contagem de ocorrências. Ao percorrer o array, se o elemento não existir na tabela hash, ele é adicionado e sua contagem é inicializada em 1; se existir, sua contagem é incrementada em 1. Ao mesmo tempo, mantenha um valor máximo e um número de modo e atualize-os ao atualizar a tabela de hash. Essa abordagem requer espaço adicional O(n), mas tem uma complexidade de tempo menor de O(n).
Aqui está um exemplo de código Java:
// 哈希表方法
public static int majorityElement2(int[] nums) {
// 初始化哈希表、最大值和众数
HashMap<Integer, Integer> counter = new HashMap<>();
int maxCount = 0;
Integer majority = null;
// 遍历数组
for (int num : nums) {
// 如果该元素在哈希表中不存在,则将其加入并初始化其次数为1;如果存在,则将其次数加1。
counter.put(num, counter.getOrDefault(num, 0) + 1);
// 如果该元素出现次数超过最大值,则更新最大值和众数。
if (counter.get(num) > maxCount) {
maxCount = counter.get(num);
majority = num;
}
}
return majority;
}
2. Método de votação de Moore
2.1 Princípio
O Algoritmo de Voto da Maioria de Boyer-Moore é um método eficiente e que economiza espaço para encontrar a maioria absoluta , que foi proposto por Robert S. Boyer e J Strother Moore em 1981.
O processo do método de votação de Moore é muito simples, imaginemos o processo de encontrar a maioria absoluta como uma eleição. Mantemos um m
, representando o candidato atual, e depois mantemos um cnt
. Para cada nova cédula, se votou no candidato atual, some cnt
1, senão cnt
subtraia 1 (talvez você possa imaginar que um torcedor fanático de B foi bater em um torcedor de A refeição, e então nenhum dos dois pode votar). Em particular, quando os votos são contados cnt=0
, podemos pensar que ninguém tem vantagem no momento, então quem quer que seja o novo voto será o novo candidato.
Por exemplo, se 5 votos forem dados para 1,3,3,2,3 respectivamente, então:
voto | candidato | contar cnt |
---|---|---|
1 | 1 | 1 |
3 | 3 | 0 |
3 | 3 | 1 |
2 | 2 | 0 |
3 | 3 | 1 |
O último candidato restante é 3, que é a maioria absoluta.
A complexidade de tempo do método de votação de Moore é O(n), porque ele só precisa percorrer o array uma vez. A complexidade do espaço é O(1), pois apenas duas variáveis são necessárias para acompanhar os candidatos e as contagens.
2.2 Extensão
A votação de Moore pode ser estendida para encontrar elementos que ocorrem mais de n/k vezes (n sendo o número de elementos).
- Em primeiro lugar, o modo de n/k é apenas k-1 no máximo, porque a definição de modo significa que o número de ocorrências é maior que n/k. Se houver k no modo, então a soma de todos os elementos do modo deve ser maior que n. não coincidem. Portanto, podemos usar uma tabela hash para registrar k-1 candidatos e suas contagens correspondentes.
- para cada novo elemento
- se já estiver na tabela hash, incremente sua contagem;
- Se não estiver na tabela de hash e a tabela de hash ainda não estiver cheia, adicione-a à tabela de hash e inicialize a contagem em 1;
- Se não estiver na tabela de hash e a tabela de hash estiver cheia, diminua a contagem de todos os candidatos em 1 e remova os candidatos com uma contagem de 0.
- Os últimos candidatos restantes são elementos que podem ocorrer mais de n/k vezes, mas precisam percorrer o array novamente para verificar se realmente atendem às condições.
O código de amostra Java é o seguinte:
// 输入:数组arr,整数k
// 输出:一个列表,包含所有出现次数超过n/k的元素
public List<Integer> mooreVoting(int[] arr, int k) {
// 初始化一个哈希表,用来存储候选人和计数
HashMap<Integer, Integer> candidates = new HashMap<>();
// 遍历数组中的每个元素
for (int x : arr) {
// 如果x已经是候选人,则增加其计数
if (candidates.containsKey(x)) {
candidates.put(x, candidates.get(x) + 1);
}
// 如果x不是候选人,并且候选人还没有满k-1个,则将x加入候选人并初始化计数为1
else if (candidates.size() < k - 1) {
candidates.put(x, 1);
}
// 如果x不是候选人,并且候选人已经满了k-1个,则将所有候选人的计数减1,并删除那些计数为0的候选人
else {
for (Integer y : new ArrayList<>(candidates.keySet())) {
candidates.put(y, candidates.get(y) - 1);
if (candidates.get(y) == 0) {
candidates.remove(y);
}
}
}
}
// 初始化一个列表,用来存储最终结果
List<Integer> result = new ArrayList<>();
// 遍历剩下的候选人,验证它们是否真的超过n/k次出现
for (Integer x : candidates.keySet()) {
int count = 0;
for (int y : arr) {
if (x == y) {
count++;
}
}
if (count > arr.length / k) {
result.add(x);
}
}
// 返回结果列表
return result;
}
2.3 Desvantagens e limitações
- O método de votação Moore só pode encontrar elementos que aparecem mais do que uma certa porcentagem , mas não pode encontrar o elemento que aparece mais . Se nenhum elemento satisfizer esta condição proporcional, então a votação de Moore pode retornar uma lista vazia ou o elemento errado. Por exemplo, no conjunto de dados {1,1,2,3}, o elemento com mais ocorrências é 1, e o método de votação Moore só consegue encontrar números com razão maior que 1/2, mas não consegue encontrar números com uma razão <=1/2.
- Outra limitação do método de votação de Moore é que ele requer um limite de porcentagem claro para determinar o número de candidatos. Se não houver tal limite, ou se este limite não for razoável, então o método de votação de Moore pode não encontrar o resultado correto.
3. Combate real do LeetCode
3.1 A maioria dos elementos
Dada uma
n
matriz de tamanhonums
, retorne a maioria de seus elementos. Um elemento majoritário é um elemento que ocorre mais de vezes na matriz⌊ n/2 ⌋
.Você pode assumir que as matrizes não estão vazias e que sempre há uma maioria de elementos em uma determinada matriz.
public int majorityElement(int[] nums) {
int m = nums[0], cnt = 1; // 初始化候选元素m和计数器cnt
for (int i = 1; i < nums.length; i++) {
// 遍历数组中的每个元素
if (cnt == 0) m = nums[i]; // 如果计数器为零,更新候选元素为当前元素
if (nums[i] == m) {
// 如果当前元素等于候选元素
cnt++; // 增加计数器
} else {
// 否则
cnt--; // 减少计数器
}
}
return m; // 返回最终的候选元素,它就是多数元素
}
3.2 A maioria dos elementos II
229. A maioria dos elementos II
Dado um array inteiro de tamanho n , encontre todos os
⌊ n/3 ⌋
elementos que aparecem mais de vezes.
public List<Integer> majorityElement(int[] nums) {
// 初始化一个哈希表,用来存储候选人和计数
HashMap<Integer, Integer> candidates = new HashMap<>();
int k = 3;
// 遍历数组中的每个元素
for (int x : nums) {
// 如果x已经是候选人,则增加其计数
if (candidates.containsKey(x)) {
candidates.put(x, candidates.get(x) + 1);
}
// 如果x不是候选人,并且候选人还没有满k-1个,则将x加入候选人并初始化计数为1
else if (candidates.size() < k - 1) {
candidates.put(x, 1);
}
// 如果x不是候选人,并且候选人已经满了k-1个,则将所有候选人的计数减1,并删除那些计数为0的候选人
else {
for (Integer y : new ArrayList<>(candidates.keySet())) {
candidates.put(y, candidates.get(y) - 1);
if (candidates.get(y) == 0) {
candidates.remove(y);
}
}
}
}
// 初始化一个列表,用来存储最终结果
List<Integer> result = new ArrayList<>();
// 遍历剩下的候选人,验证它们是否真的超过n/k次出现
for (Integer x : candidates.keySet()) {
int count = 0;
for (int y : nums) {
if (x == y) {
count++;
}
}
if (count > nums.length / k) {
result.add(x);
}
}
// 返回结果列表
return result;
}