Elemento mayoritario: votación de Boyer-Moore
Encuentre el elemento que aparece más de n/2 veces mediante el algoritmo de votación de Boyer-Moore, de tiempo lineal y espacio O(1), y demuestre su corrección.
Elemento mayoritario: votación de Boyer-Moore es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
El problema del elemento mayoritario
Elemento mayoritario (LeetCode 169): encuentre el elemento que aparece más de n/2 veces en un array de longitud n. El elemento mayoritario siempre existe, según la garantía del problema. Para [3, 2, 3], la respuesta es 3. Para [2, 2, 1, 1, 1, 2, 2], la respuesta es 2 (aparece 4 veces de un total de 7). Las estrategias van desde ordenar en O(n log n) hasta el elegante algoritmo de votación de Boyer-Moore, que funciona en O(n) y usa O(1) espacio.
# The majority element appears MORE than n/2 times
# So it appears more than all other elements COMBINED
examples = [
[3, 2, 3], # 3 appears 2/3 times > 1/2
[2, 2, 1, 1, 1, 2, 2], # 2 appears 4/7 times > 3.5
[1], # trivially 1
[1, 1, 2, 1], # 1 appears 3/4 times
]
for e in examples:
from collections import Counter
c = Counter(e)
print(f'Array: {e} → majority: {max(c, key=c.get)} (count {max(c.values())})')Enfoques anteriores a Boyer-Moore
Tres enfoques previos al óptimo: (1) Ordenación: ordene el array; el elemento central siempre es el mayoritario (ya que aparece >n/2 veces). O(n log n), espacio O(1). (2) Tabla hash: cuente las frecuencias y devuelva el elemento cuyo recuento sea > n/2. Tiempo O(n), espacio O(n). (3) Muestreo aleatorio: elija un elemento al azar y compruebe que aparece >n/2 veces; se esperan O(1) intentos (el elemento mayoritario se elige con una probabilidad >1/2). Boyer-Moore logra tiempo O(n) y espacio O(1) de forma determinista.
from collections import Counter
def majority_sort(nums):
nums.sort()
return nums[len(nums) // 2] # middle is always majority
def majority_hashmap(nums):
count = Counter(nums)
return max(count, key=count.get)
def majority_random(nums):
import random
n = len(nums)
while True:
candidate = random.choice(nums)
if nums.count(candidate) > n // 2:
return candidate
nums = [2, 2, 1, 1, 1, 2, 2]
print(majority_sort(nums[:])) # 2
print(majority_hashmap(nums)) # 2Algoritmo de votación de Boyer-Moore
El algoritmo de votación de Boyer-Moore mantiene un candidate y un count. Recorra el array: si count == 0, establezca el elemento actual como el nuevo candidato. Si el elemento actual coincide con el candidato, incremente count. De lo contrario, disminuya count. Al final, el candidato es el elemento mayoritario. Esto funciona porque el elemento mayoritario aparece más veces que todos los demás combinados: nunca puede quedar completamente descartado mediante votación.
def majority_element(nums):
candidate = None
count = 0
for num in nums:
if count == 0:
candidate = num # new candidate
if num == candidate:
count += 1
else:
count -= 1
return candidate
print(majority_element([3, 2, 3])) # 3
print(majority_element([2, 2, 1, 1, 1, 2, 2])) # 2
print(majority_element([1])) # 1Intuición tras el algoritmo
Intuición: imagine que cada elemento «cancela» una aparición de un elemento diferente. El elemento mayoritario (count > n/2) tiene más apariciones que todos los demás combinados, por lo que puede cancelar todos los elementos no mayoritarios y aún conservar apariciones restantes. La variable count registra la ventaja neta del candidato actual. Cuando count llega a 0, el candidato actual ha sido cancelado por el mismo número de elementos opuestos; quien aparezca después se convierte en el nuevo candidato.
def bm_trace(nums):
candidate = count = 0
for i, num in enumerate(nums):
if count == 0:
candidate = num
old_count = count
if num == candidate: count += 1
else: count -= 1
print(f'num={num}: candidate={candidate}, count: {old_count}→{count}')
return candidate
bm_trace([2, 2, 1, 1, 1, 2, 2])
# 2→c=1, 2→c=2, 1→c=1, 1→c=0, 1→new cand=1 c=1, 2→c=0, 2→new cand=2 c=1Demostración de corrección
Demostración: sea m el elemento mayoritario, con un recuento k > n/2. Al final del algoritmo, ¿puede un elemento no mayoritario ser el candidato? Para que ocurra, m debe haberse cancelado por completo. Cada cancelación de m cuesta una aparición de algún otro elemento. Para cancelar las k apariciones de m, se necesitan al menos k apariciones de elementos distintos de m. Pero k > n/2 y el total de elementos distintos de m es n-k < n/2 < k. Contradicción: m no puede cancelarse por completo.
# Proof by contradiction visualised:
# Array: [M, M, M, A, B, A, B] (M is majority, 4/7 times)
# Cancellations: M-A, M-B, M-A, M-B would need 4 non-M elements
# But there are only 4 non-M elements and 4 M's > n/2 = 3.5
# So M can survive: after cancellations, at least 1 M remains uncancelled
def verify_bm(tests):
for nums in tests:
result = majority_element(nums)
brute = max(set(nums), key=nums.count)
assert result == brute, f'Mismatch: {nums} → BM={result}, Brute={brute}'
print('All tests passed!')
def majority_element(nums):
c = cnt = 0
for n in nums:
if cnt == 0: c = n
cnt += 1 if n == c else -1
return c
verify_bm([[1],[3,2,3],[1,1,2,1],[2,2,1,1,1,2,2]])Elemento mayoritario II: más de n/3
Elemento mayoritario II (LeetCode 229): encuentre todos los elementos que aparecen más de n/3 veces. Como máximo, 2 elementos pueden cumplir esta condición (ya que 3 × n/3 = n). Extienda Boyer-Moore para mantener dos candidatos con dos contadores. Cuando un elemento nuevo no coincide con ningún candidato y ambos contadores son positivos, disminuya ambos. Una pasada final de verificación confirma cuáles de los candidatos superan realmente n/3.
def majority_element_ii(nums):
cand1 = cand2 = None
count1 = count2 = 0
for num in nums:
if num == cand1: count1 += 1
elif num == cand2: count2 += 1
elif count1 == 0: cand1, count1 = num, 1
elif count2 == 0: cand2, count2 = num, 1
else:
count1 -= 1
count2 -= 1
# Verify: candidates must exceed n/3
n = len(nums)
return [c for c in [cand1, cand2]
if c is not None and nums.count(c) > n // 3]
print(majority_element_ii([3, 2, 3])) # [3]
print(majority_element_ii([1, 2])) # [1, 2]
print(majority_element_ii([1, 1, 1, 3, 3, 2, 2, 2])) # [1, 2]Boyer-Moore generalizado: mayoría n/k
Boyer-Moore se generaliza para encontrar todos los elementos que aparecen más de n/k veces utilizando k-1 candidatos. Como máximo, k-1 elementos pueden cumplir esta condición. Mantenga k-1 pares (candidate, count). Cuando ninguno coincida y todos los contadores sean positivos, disminuya todos los contadores en 1. Este algoritmo generalizado se ejecuta en tiempo O(n) y espacio O(k). En las entrevistas técnicas, suele ser suficiente conocer la extensión de dos candidatos para n/3.
def majority_nk(nums, k):
'''Find all elements appearing more than n/k times.'''
counts = {} # candidate -> count
for num in nums:
counts[num] = counts.get(num, 0) + 1
if len(counts) >= k:
# Remove all candidates by decrementing
new_counts = {c: cnt-1 for c, cnt in counts.items() if cnt > 1}
counts = new_counts
# Verify
threshold = len(nums) // k
return [c for c in counts if nums.count(c) > threshold]
print(majority_nk([1,2,3,1,2,1,2,1], 3)) # [1, 2] (both > 8/3 ≈ 2.67)
print(majority_nk([1,1,1,2,2,3,3,3], 4)) # [1, 3] (both > 8/4 = 2)Elemento mayoritario mediante divide y vencerás
Un enfoque de D&C: divida el array por la mitad. El elemento mayoritario del array completo debe ser mayoritario en al menos una de las mitades (si no es mayoritario en ninguna, no puede aparecer más de n/2 veces en total). Encuentre recursivamente el elemento mayoritario de cada mitad. Si ambas mitades coinciden, esa es la respuesta. En caso contrario, cuente ambos candidatos en todo el array y devuelva el que tenga más apariciones. Relación de recurrencia: T(n) = 2T(n/2) + O(n) → O(n log n).
def majority_dc(nums, lo=None, hi=None):
if lo is None: lo, hi = 0, len(nums) - 1
if lo == hi: return nums[lo]
mid = (lo + hi) // 2
left_maj = majority_dc(nums, lo, mid)
right_maj = majority_dc(nums, mid + 1, hi)
if left_maj == right_maj:
return left_maj
# Count both candidates across the sub-range
left_count = sum(1 for i in range(lo, hi+1) if nums[i] == left_maj)
right_count = sum(1 for i in range(lo, hi+1) if nums[i] == right_maj)
return left_maj if left_count > right_count else right_maj
print(majority_dc([3, 2, 3])) # 3
print(majority_dc([2, 2, 1, 1, 1, 2, 2])) # 2Boyer-Moore frente a otros métodos
Comparación de métodos para el elemento mayoritario: Ordenación: tiempo O(n log n), espacio O(1), destructiva. Tabla hash: tiempo O(n), espacio O(n), no destructiva. D&C: tiempo O(n log n), espacio O(log n) para la pila de llamadas. Boyer-Moore: tiempo O(n), espacio O(1), una sola pasada, no destructivo. Boyer-Moore es estrictamente superior para este problema. En las entrevistas, empiece siempre por Boyer-Moore después de mencionar brevemente el enfoque más sencillo con una tabla hash.
import time, random
nums = [random.randint(1, 100) for _ in range(500000)]
# Make element 42 the majority
nums = [42] * 300000 + nums[:200000]
random.shuffle(nums)
start = time.time()
from collections import Counter
hm = Counter(nums).most_common(1)[0][0]
print(f'HashMap: {hm} in {time.time()-start:.4f}s')
def bm(nums):
c = cnt = 0
for n in nums:
if cnt == 0: c = n
cnt += 1 if n == c else -1
return c
start = time.time()
result = bm(nums)
print(f'Boyer-Moore: {result} in {time.time()-start:.4f}s')
print(f'Both correct: {hm == result}')Cuando no se garantiza la existencia de un elemento mayoritario
Boyer-Moore siempre devuelve un candidato, pero este puede no ser un elemento mayoritario si no existe ninguno. Si el problema no garantiza la existencia de un elemento mayoritario, debe verificarlo: después de Boyer-Moore, cuente las apariciones del candidato. Si count > n/2, es el elemento mayoritario. De lo contrario, devuelva -1 o None. Esta verificación añade otra pasada O(n), pero mantiene el algoritmo general en tiempo O(n) y espacio O(1).
def majority_element_safe(nums):
'''Returns majority element or None if it doesn't exist.'''
# Phase 1: find candidate
candidate = count = 0
for num in nums:
if count == 0:
candidate = num
count += 1 if num == candidate else -1
# Phase 2: verify
if nums.count(candidate) > len(nums) // 2:
return candidate
return None
print(majority_element_safe([3, 2, 3])) # 3 (majority exists)
print(majority_element_safe([1, 2, 3])) # None (no majority)
print(majority_element_safe([1, 2, 1, 2])) # None (tie, neither > n/2)Guía para la entrevista técnica
Enfoque para una entrevista sobre el elemento mayoritario: (1) Mencione la ordenación (O(n log n), O(1)) y la tabla hash (O(n), O(n)) como enfoques iniciales. (2) Presente Boyer-Moore como la solución óptima O(n) O(1). (3) Explique la intuición de cancelación: el elemento mayoritario no puede cancelarse porque tiene más apariciones que todos los demás combinados. (4) Escriba el código de forma clara en 5 líneas. (5) Trate el caso límite: si no se garantiza la existencia de un elemento mayoritario, añada una pasada de verificación. Esta estructura demuestra un razonamiento sistemático bajo presión temporal.
# Clean 5-line Boyer-Moore for interviews
def majority_element(nums):
c, cnt = nums[0], 1
for n in nums[1:]:
cnt += (1 if n == c else -1)
if cnt == 0: c, cnt = n, 1
return c
# Verification (if majority not guaranteed)
def majority_with_check(nums):
c = majority_element(nums)
return c if nums.count(c) > len(nums) // 2 else -1
print(majority_element([3, 2, 3])) # 3
print(majority_element([2, 2, 1, 1, 1, 2, 2])) # 2
print('Time: O(n), Space: O(1)')Comprobación rápida
Ponga a prueba su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.
Resumen de la lección
En esta lección ha aprendido: la votación de Boyer-Moore encuentra el elemento mayoritario en tiempo O(n) y espacio O(1) utilizando un candidato y un recuento que cancelan los elementos no mayoritarios, el algoritmo se extiende al caso de mayoría n/3 con dos candidatos y requiere una pasada de verificación cuando no se garantiza la existencia de un elemento mayoritario, y la demostración se basa en el hecho de que el elemento mayoritario tiene más apariciones que todos los demás elementos combinados, lo que hace imposible su cancelación completa. A continuación abordaremos la mediana de dos arrays ordenados mediante búsqueda binaria sobre el límite de partición.
Preguntas frecuentes
¿La lección «Elemento mayoritario: votación de Boyer-Moore» es gratis?
Sí — el texto completo de «Elemento mayoritario: votación de Boyer-Moore» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Elemento mayoritario: votación de Boyer-Moore»?
Encuentre el elemento que aparece más de n/2 veces mediante el algoritmo de votación de Boyer-Moore, de tiempo lineal y espacio O(1), y demuestre su corrección. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Elemento mayoritario: votación de Boyer-Moore»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Plantilla de divide y vencerás
- Contar inversiones con merge sort modificado
- Elemento mayoritario: votación de Boyer-Moore
- Mediana de dos arrays ordenados