Revisión del método de votación de Moore de la estructura de datos y el algoritmo

prefacio

En estadística, la moda es un importante indicador de tendencia central, que representa el valor que ocurre con mayor frecuencia en un conjunto de datos. Además, en un conjunto, si un elemento aparece más que la suma de todos los demás elementos, entonces se llama el modo absoluto del conjunto (equivalentemente, el modo absoluto tiene más ocurrencias que la mitad total de elementos del número). Por ejemplo, en el conjunto de datos {1,2,3,3,4}, 3 es la moda porque aparece dos veces, mientras que los demás valores aparecen una sola vez, pero no hay una moda absoluta .

Encontrar la moda puede ayudarnos a comprender las principales características y distribución de los datos, y en algunos casos también puede usarse como un valor representativo o aproximado de los datos. Por ejemplo,

  • En las encuestas de opinión, podemos centrarnos en qué candidato tiene más apoyo;
  • En las revisiones de productos, podemos prestar atención a qué calificaciones ocupan la mayoría;
  • En el procesamiento de imágenes, nos puede interesar qué color o valor de escala de grises se presenta con mayor frecuencia.

Existen diferentes métodos y algoritmos para encontrar la mayoría, y en este artículo presentaremos dos métodos comunes: fuerza bruta y votación de Moore.

  • El método de fuerza bruta cuenta el número de ocurrencias de cada elemento atravesando o hashing, y luego encuentra el elemento correspondiente al valor máximo. Este método es simple e intuitivo, pero requiere espacio adicional para almacenar cada elemento y su frecuencia, y tiene una alta complejidad temporal o espacial.
  • El método de votación de Moore mantiene un elemento candidato y un contador, actualiza el elemento candidato y el contador al atravesar la matriz y, finalmente, comprueba si el elemento candidato es una mayoría. Este método aprovecha inteligentemente la condición de que hay un elemento que representa más de la mitad (u otros umbrales) en la matriz, no requiere espacio adicional y tiene una complejidad de tiempo baja, pero este algoritmo no puede encontrar el modo general . , puede encontrar el modo absoluto .

Este artículo presentará los principios, la implementación, la expansión y las limitaciones del método de violencia y el método de votación de Moore, y brindará preguntas y respuestas relacionadas con LeetCode.

1. La Ley de la Violencia

El método de fuerza bruta es un método simple e intuitivo para encontrar la mayoría. Su idea básica es atravesar cada elemento de la matriz, contar el número de ocurrencias del elemento y luego encontrar el elemento correspondiente al valor máximo. Este enfoque se puede implementar de dos formas: una con bucles anidados y la otra con una tabla hash.

1.1 Bucles anidados

El método de los bucles anidados consiste en recorrer cada elemento de la matriz en el bucle exterior, contar el número de apariciones del elemento en el bucle interior y compararlo con el valor máximo actual. Si el elemento ocurre más que el máximo actual, actualice el máximo y el modo. Este método no requiere espacio adicional, pero tiene una mayor complejidad temporal de O(n^2).

Aquí hay un ejemplo 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 Tabla hash

El enfoque de la tabla hash (o diccionario) consiste en utilizar una estructura de datos para almacenar cada elemento y su recuento de ocurrencias. Al recorrer la matriz, si el elemento no existe en la tabla hash, se agrega y su cuenta se inicializa en 1; si existe, su cuenta aumenta en 1. Al mismo tiempo, mantenga un valor máximo y un número de modo, y actualícelos cuando actualice la tabla hash. Este enfoque requiere O(n) espacio adicional, pero tiene una menor complejidad temporal de O(n).

Aquí hay un ejemplo 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 votación de Moore

2.1 Principio

El algoritmo de voto mayoritario de Boyer-Moore es un método eficiente y que ahorra espacio para encontrar la mayoría absoluta , propuesto por Robert S. Boyer y J Strother Moore en 1981.

El proceso del método de votación de Moore es muy simple, imaginemos el proceso de encontrar la mayoría absoluta como una elección. Mantenemos uno m, que representa al candidato actual, y luego mantenemos uno cnt. Por cada nueva boleta, si votó por el candidato actual, suma cnt1, en caso contrario cntresta 1 (tal vez te imagines que un fanático de B fue a darle una paliza a un partidario de A comida, y luego ninguno puede votar). En particular, cuando se cuentan los votos cnt=0, podemos pensar que nadie tiene una ventaja en este momento, por lo que quienquiera que emita el nuevo voto se convertirá en el nuevo candidato.

Por ejemplo, si se emiten 5 votos para 1,3,3,2,3 respectivamente, entonces:

votar candidato contar centavo
1 1 1
3 3 0
3 3 1
2 2 0
3 3 1

El último candidato que queda es el 3, que es la mayoría absoluta.

La complejidad temporal del método de votación de Moore es O(n), porque solo necesita atravesar la matriz una vez. La complejidad del espacio es O(1) ya que solo se necesitan dos variables para realizar un seguimiento de los candidatos y los recuentos.

2.2 Extensión

La votación de Moore se puede extender para encontrar elementos que ocurren más de n/k veces (siendo n el número de elementos).

  • En primer lugar, el modo de n/k es solo k-1 como máximo, porque la definición de modo significa que el número de ocurrencias es mayor que n/k. Si hay k en el modo, entonces la suma de todos los elementos de la moda debe ser mayor que n no coinciden. Por lo tanto, podemos usar una tabla hash para registrar los candidatos k-1 y sus conteos correspondientes.
  • por cada nuevo elemento
    • si ya está en la tabla hash, incremente su conteo;
    • Si no está en la tabla hash y la tabla hash aún no está llena, agréguelo a la tabla hash e inicialice el conteo a 1;
    • Si no está en la tabla hash y la tabla hash está llena, disminuya los recuentos de todos los candidatos en 1 y elimine los candidatos con un recuento de 0.
  • Los últimos candidatos que quedan son elementos que pueden ocurrir más de n/k veces, pero necesitan atravesar la matriz nuevamente para verificar que realmente cumplan con las condiciones.

El código de muestra de Java es el siguiente:

// 输入:数组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 Desventajas y limitaciones

  • El método de votación de Moore sólo puede encontrar elementos que aparecen más de un cierto porcentaje , pero no puede encontrar el elemento que aparece más . Si ningún elemento satisface esta condición proporcional, entonces la votación de Moore puede devolver una lista vacía o el elemento incorrecto. Por ejemplo, en el conjunto de datos {1,1,2,3}, el elemento con más ocurrencias es 1, y el método de votación de Moore solo puede encontrar números con una proporción mayor que 1/2, pero no puede encontrar números con una relación <=1/2.
  • Otra limitación del método de votación de Moore es que requiere un umbral de porcentaje claro para determinar el número de candidatos. Si no existe tal umbral, o si este umbral no es razonable, es posible que el método de votación de Moore no encuentre el resultado correcto.

3. Combate real LeetCode

3.1 La mayoría de los elementos

169. La mayoría de los elementos

Dada una nmatriz de tamaño nums, devuelve la mayoría de sus elementos. Un elemento mayoritario es un elemento que aparece más de veces en el arreglo ⌊ n/2 ⌋.

Puede suponer que las matrices no están vacías y que siempre hay una mayoría de elementos en una matriz determinada.

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 La mayoría de los elementos II

229. La mayoría de los elementos II

Dada una matriz de enteros de tamaño n , encuentre todos ⌊ n/3 ⌋los elementos que aparecen más de veces.

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;
}

referencia

  1. Notas de estudio de algoritmos (78): votación de Moore

Supongo que te gusta

Origin blog.csdn.net/qq_23091073/article/details/129641989
Recomendado
Clasificación