Élément majoritaire : vote de Boyer-Moore
Trouvez l’élément apparaissant plus de n/2 fois à l’aide de l’algorithme de vote de Boyer-Moore, en temps linéaire et avec un espace O(1), puis démontrez sa correction.
Élément majoritaire : vote de Boyer-Moore est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.
Problème de l’élément majoritaire
Élément majoritaire (LeetCode 169) : trouvez l’élément qui apparaît plus de n/2 fois dans un tableau de longueur n. L’existence de l’élément majoritaire est garantie par l’énoncé du problème. Pour [3, 2, 3], la réponse est 3. Pour [2, 2, 1, 1, 1, 2, 2], la réponse est 2 (il apparaît 4 fois sur 7). Les approches vont du tri en O(n log n) à l’élégant algorithme de vote de Boyer-Moore en O(n) et O(1).
# 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())})')Approches avant Boyer-Moore
Trois approches avant la solution optimale : (1) Tri : triez le tableau ; l’élément du milieu est toujours l’élément majoritaire (puisqu’il apparaît >n/2 fois). O(n log n), espace O(1). (2) Table de hachage : comptez les fréquences et renvoyez l’élément dont le compteur est > n/2. Temps O(n), espace O(n). (3) Échantillonnage aléatoire : choisissez un élément au hasard et vérifiez qu’il apparaît >n/2 fois ; le nombre prévu d’essais est O(1) (l’élément majoritaire est choisi avec une probabilité >1/2). Boyer-Moore atteint un temps O(n) et un espace O(1) de manière déterministe.
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)) # 2Algorithme de vote de Boyer-Moore
L’algorithme de vote de Boyer-Moore conserve un candidate et un count. Parcourez le tableau : si count == 0, définissez l’élément courant comme nouveau candidat. Si l’élément courant correspond au candidat, incrémentez le compteur. Sinon, décrémentez-le. À la fin, le candidat est l’élément majoritaire. Cela fonctionne parce que l’élément majoritaire apparaît plus souvent que tous les autres réunis : il ne peut donc jamais être complètement éliminé par les votes.
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])) # 1Intuition derrière l’algorithme
Intuition : imaginez que chaque élément « annule » une occurrence d’un élément différent. L’élément majoritaire (compteur > n/2) possède plus d’occurrences que tous les autres réunis ; il peut donc annuler tous les éléments non majoritaires et conserver encore des occurrences. La variable count suit l’avance nette du candidat actuel. Lorsque count atteint 0, le candidat actuel a été annulé par autant d’éléments adverses ; celui qui apparaît ensuite devient le nouveau candidat.
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=1Preuve de correction
Preuve : soit m l’élément majoritaire, dont le nombre d’occurrences est k > n/2. À la fin de l’algorithme, un élément non majoritaire peut-il être le candidat ? Pour cela, m doit avoir été complètement annulé. Chaque annulation de m coûte une occurrence d’un autre élément. Pour annuler les k occurrences de m, il faut au moins k occurrences d’éléments différents de m. Or k > n/2 et le nombre total d’éléments différents de m est n-k < n/2 < k. Contradiction : m ne peut pas être complètement annulé.
# 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]])Élément majoritaire II : plus de n/3
Élément majoritaire II (LeetCode 229) : trouvez tous les éléments apparaissant plus de n/3 fois. Au plus 2 éléments peuvent satisfaire cette condition (puisque 3 × n/3 = n). Étendez Boyer-Moore en conservant deux candidats avec deux compteurs. Lorsqu’un nouvel élément ne correspond à aucun candidat et que les deux compteurs sont positifs, décrémentez-les tous les deux. Une passe finale de vérification confirme quels candidats dépassent réellement 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 généralisé : majorité n/k
Boyer-Moore se généralise pour trouver tous les éléments apparaissant plus de n/k fois en utilisant k-1 candidats. Au plus k-1 éléments peuvent satisfaire cette condition. Conservez k-1 paires (candidat, compteur). Lorsqu’aucune paire ne correspond et que tous les compteurs sont positifs, décrémentez tous les compteurs de 1. Cet algorithme généralisé s’exécute en temps O(n) et utilise un espace O(k). En entretien, il suffit généralement de connaître l’extension à deux candidats pour 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)Élément majoritaire par division et conquête
Une approche par division et conquête : divisez le tableau en deux. L’élément majoritaire du tableau complet doit être majoritaire dans au moins une moitié (s’il n’est majoritaire dans aucune des deux, il ne peut pas apparaître plus de n/2 fois au total). Trouvez récursivement l’élément majoritaire de chaque moitié. Si les deux moitiés donnent le même élément, c’est la réponse. Sinon, comptez les deux candidats dans le tableau complet et renvoyez celui qui possède le plus d’occurrences. Récurrence : 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 et les autres méthodes
Comparaison des méthodes pour l’élément majoritaire : Tri : temps O(n log n), espace O(1), destructif. Table de hachage : temps O(n), espace O(n), non destructif. Division et conquête : temps O(n log n), espace O(log n) pour la pile d’appels. Boyer-Moore : temps O(n), espace O(1), un seul parcours, non destructif. Boyer-Moore est strictement supérieur pour ce problème. En entretien, commencez toujours par présenter Boyer-Moore après avoir brièvement mentionné l’approche plus simple par table de hachage.
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}')Lorsqu’aucun élément majoritaire n’est garanti
Boyer-Moore renvoie toujours un candidat, mais celui-ci n’est peut-être pas un élément majoritaire si aucun n’existe. Si l’énoncé ne garantit pas l’existence d’un élément majoritaire, vous devez effectuer une vérification : après Boyer-Moore, comptez les occurrences du candidat. Si le compteur est > n/2, il s’agit de l’élément majoritaire. Sinon, renvoyez -1 ou None. Cette vérification ajoute une autre passe en O(n), mais l’algorithme complet reste en temps O(n) et en espace 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)Déroulement d’un entretien
Approche à adopter en entretien pour l’élément majoritaire : (1) mentionnez le tri (O(n log n), O(1)) et la table de hachage (O(n), O(n)) comme approches initiales. (2) Présentez Boyer-Moore comme solution optimale en O(n) et O(1). (3) Expliquez l’intuition de l’annulation : l’élément majoritaire ne peut pas être annulé, car il possède plus d’occurrences que tous les autres réunis. (4) Écrivez l’algorithme proprement en 5 lignes. (5) Traitez le cas limite suivant : si l’existence d’un élément majoritaire n’est pas garantie, ajoutez une passe de vérification. Cette structure montre une réflexion systématique sous la contrainte du temps.
# 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)')Vérification rapide
Testez votre compréhension des concepts de Structures de données & algorithmes — préparation aux entretiens de programmation présentés dans cette leçon.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris que : le vote de Boyer-Moore trouve l’élément majoritaire en temps O(n) et en espace O(1) à l’aide d’un candidat et d’un compteur qui annulent les éléments non majoritaires, l’algorithme se généralise à la majorité n/3 avec deux candidats et nécessite une passe de vérification lorsque l’existence d’un élément majoritaire n’est pas garantie, et la preuve repose sur le fait que l’élément majoritaire possède plus d’occurrences que tous les autres éléments réunis, ce qui rend son annulation complète impossible. Ensuite, nous aborderons la médiane de deux tableaux triés à l’aide d’une recherche binaire sur la frontière de partition.
Questions Fréquemment Posées
La leçon « Élément majoritaire : vote de Boyer-Moore » est-elle gratuite ?
Oui — le texte complet de « Élément majoritaire : vote de Boyer-Moore » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Élément majoritaire : vote de Boyer-Moore » ?
Trouvez l’élément apparaissant plus de n/2 fois à l’aide de l’algorithme de vote de Boyer-Moore, en temps linéaire et avec un espace O(1), puis démontrez sa correction. Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?
Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Élément majoritaire : vote de Boyer-Moore » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?
Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Modèle diviser pour régner
- Compter les inversions avec un tri fusion modifié
- Élément majoritaire : vote de Boyer-Moore
- Médiane de deux tableaux triés