0Pricing
DSA Interview Prep · Leçon

Recherche binaire dans des tableaux pivotés et non triés

Résolvez search-in-rotated-sorted-array et find-minimum-in-rotated-array en déterminant quelle moitié est triée à chaque étape.

Recherche binaire dans des tableaux pivotés et non triés est une leçon DSA Interview Prep gratuite sur CoddyKit. Ceci est la leçon 2 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 DSA Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours DSA Interview Prep comprend 4 leçons au total.

Qu’est-ce qu’un tableau trié pivoté ?

Un tableau trié pivoté est un tableau trié qui a été coupé à un certain pivot, puis dont les deux parties ont été interverties. Par exemple, [4, 5, 6, 7, 0, 1, 2] est le tableau trié [0,1,2,4,5,6,7] pivoté à l’indice 4. La recherche binaire standard échoue dans ce cas, car le tableau n’est plus trié globalement.

L’idée essentielle est qu’au moins une moitié du tableau est toujours triée après toute rotation. Votre recherche binaire doit déterminer quelle moitié est triée avant de décider comment déplacer les bornes.

# A rotated sorted array — one half is always sorted
arr = [4, 5, 6, 7, 0, 1, 2]
# Left half [4,5,6,7] is sorted
# Right half [0,1,2] is also sorted
# But left[0]=4 > right[-1]=2 => rotation happened in left-to-right crossing

Identifier la moitié triée

Après avoir calculé mid, comparez arr[lo] à arr[mid]. Si arr[lo] <= arr[mid], la moitié gauche est triée ; sinon, la moitié droite est triée. Une fois que vous savez quelle moitié est triée, vous pouvez vérifier si la cible appartient à cette plage triée et restreindre la recherche en conséquence.

Cet arbre de décision vous permet d’éliminer exactement la moitié du tableau à chaque étape, tout en conservant la complexité O(log n), même dans un tableau ayant subi une rotation.

def search_rotated(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] == target:
            return mid
        # Left half is sorted
        if nums[lo] <= nums[mid]:
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        # Right half is sorted
        else:
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

print(search_rotated([4, 5, 6, 7, 0, 1, 2], 0))  # 4
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 3))  # -1

Suivre un exemple pas à pas

Suivons pas à pas l’exécution de search_rotated([4,5,6,7,0,1,2], 0). Au départ, lo=0, hi=6, mid=3, arr[mid]=7. La cible 0 se trouve-t-elle dans la moitié gauche triée [4..7] ? Non, nous définissons donc lo=4. À présent, lo=4, hi=6, mid=5, arr[mid]=1. La moitié gauche [0,1] est triée (arr[lo]=0 <= arr[mid]=1). 0 se trouve-t-il dans [0..1) ? Oui, nous définissons donc hi=4. À présent, lo=4, hi=4, mid=4, arr[4]=0 — trouvé à l’indice 4.

# Step-by-step trace
nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
steps = []
lo, hi = 0, len(nums) - 1
while lo <= hi:
    mid = lo + (hi - lo) // 2
    steps.append(f'lo={lo} hi={hi} mid={mid} val={nums[mid]}')
    if nums[mid] == target:
        steps.append(f'Found at {mid}')
        break
    if nums[lo] <= nums[mid]:
        if nums[lo] <= target < nums[mid]:
            hi = mid - 1
        else:
            lo = mid + 1
    else:
        if nums[mid] < target <= nums[hi]:
            lo = mid + 1
        else:
            hi = mid - 1
for s in steps:
    print(s)

Gérer les doublons lors d’une rotation

Lorsque le tableau ayant subi une rotation peut contenir des doublons (par exemple, [1,3,1,1,1]), la condition nums[lo] == nums[mid] est ambiguë : vous ne pouvez pas déterminer quelle moitié est triée. La solution sûre consiste à incrémenter lo (ou à décrémenter hi) d’une unité, puis à réessayer. Dans le pire des cas, cela dégrade le temps d’exécution à O(n), ce que vous devez mentionner à la personne qui vous interroge.

def search_rotated_with_dups(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] == target:
            return True
        # Ambiguous: shrink left boundary
        if nums[lo] == nums[mid] == nums[hi]:
            lo += 1
            hi -= 1
        elif nums[lo] <= nums[mid]:
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return False

print(search_rotated_with_dups([1, 3, 1, 1, 1], 3))  # True
print(search_rotated_with_dups([2, 2, 2, 0, 2], 0))  # True

Trouver le minimum d’un tableau trié après rotation

Un problème connexe consiste à trouver le plus petit élément d’un tableau trié après rotation, sans rechercher une cible précise. Le minimum se trouve toujours dans la moitié non triée. À chaque étape : si arr[mid] > arr[hi], le minimum se trouve dans la moitié droite (lo = mid + 1) ; sinon, il se trouve dans la moitié gauche, en incluant mid (hi = mid). Lorsque lo == hi, vous avez trouvé le minimum.

def find_min(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1   # min is in right half
        else:
            hi = mid       # min is at mid or left of mid
    return nums[lo]

print(find_min([3, 4, 5, 1, 2]))   # 1
print(find_min([4, 5, 6, 7, 0, 1, 2]))  # 0
print(find_min([11, 13, 15, 17]))  # 11 (no rotation)

Pourquoi arr[lo] <= arr[mid] détecte la moitié gauche triée

La condition arr[lo] <= arr[mid] fonctionne car, dans un segment trié (ou trié sans rotation), le premier élément est toujours le plus petit. Si arr[lo] <= arr[mid], aucune rotation n’a eu lieu dans [lo..mid] ; cette moitié est donc triée. L’égalité prend en charge le cas où lo == mid (un segment à un seul élément est naturellement trié).

Inversement, si arr[lo] > arr[mid], le pivot de rotation doit se trouver entre lo et mid, ce qui signifie que la moitié droite [mid..hi] constitue le segment trié contigu.

# Visualise: detect which half is sorted
examples = [
    ([4, 5, 6, 7, 0, 1, 2], 0, 6),  # mid=3, val=7 => left sorted
    ([6, 7, 0, 1, 2, 4, 5], 0, 6),  # mid=3, val=1 => right sorted
]
for arr, lo, hi in examples:
    mid = lo + (hi - lo) // 2
    if arr[lo] <= arr[mid]:
        print(f'arr[{lo}]={arr[lo]} <= arr[{mid}]={arr[mid]}  => LEFT half sorted')
    else:
        print(f'arr[{lo}]={arr[lo]} >  arr[{mid}]={arr[mid]}  => RIGHT half sorted')

Analyse de la complexité

La recherche binaire dans un tableau trié après rotation conserve un temps d’exécution de O(log n) et un espace de O(1), car nous divisons toujours l’espace de recherche par deux à chaque itération. La seule différence par rapport à la recherche binaire classique est une vérification supplémentaire en temps constant pour déterminer quelle moitié est triée.

Avec des doublons, le pire des cas se dégrade à O(n), car nous pouvons n’incrémenter lo que d’une unité à chaque étape. Mentionnez explicitement ce compromis : cela montre que vous réfléchissez aux cas limites au-delà du chemin nominal.

Parcours de LeetCode 33

LeetCode 33, « Recherche dans un tableau trié après rotation », est la forme canonique de ce problème. Les contraintes garantissent l’absence de doublons et exactement une rotation. La solution est la fonction search_rotated que nous avons écrite précédemment. Points clés pour l’entretien : énoncez toujours l’hypothèse d’absence de doublons, vérifiez vos inégalités avec un exemple concret à la limite, et confirmez que l’indice renvoyé est correct, que la cible soit trouvée ou non.

# LeetCode 33 — complete solution
def search(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] == target:
            return mid
        if nums[lo] <= nums[mid]:        # left half sorted
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:                            # right half sorted
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

# Tests
print(search([4,5,6,7,0,1,2], 0))   # 4
print(search([4,5,6,7,0,1,2], 3))   # -1
print(search([1], 0))               # -1

LeetCode 153 : trouver le minimum sans doublons

LeetCode 153, « Trouver le minimum dans un tableau trié après rotation », demande de trouver le minimum sans doublons. L’approche consiste à comparer arr[mid] à arr[hi] (et non à arr[lo]) pour déterminer de quel côté se trouve le minimum. Si arr[mid] > arr[hi], le minimum se trouve à droite ; sinon, il se trouve à mid ou à gauche. Cette méthode converge vers le minimum en O(log n).

def findMin(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1
        else:
            hi = mid
    return nums[lo]

print(findMin([3,4,5,1,2]))         # 1
print(findMin([4,5,6,7,0,1,2]))     # 0
print(findMin([11,13,15,17]))       # 11

Nombre de rotations et indice du pivot

Une fois que vous savez trouver le plus petit élément, vous connaissez également le nombre de rotations : l’indice du minimum correspond exactement au nombre de positions dont le tableau a été décalé vers la droite. Par exemple, dans [4,5,6,7,0,1,2], le minimum se trouve à l’indice 4 ; le tableau a donc subi une rotation de 4 positions.

Connaître le pivot vous permet d’appliquer une recherche binaire standard en considérant les indices modulo n : real_idx = (mid + pivot) % n. Cette autre formulation peut simplifier le raisonnement lorsque vous travaillez avec des structures indexées circulairement.

def search_via_pivot(nums, target):
    n = len(nums)
    # Find pivot (index of minimum)
    lo, hi = 0, n - 1
    while lo < hi:
        mid = lo + (hi - lo) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1
        else:
            hi = mid
    pivot = lo
    # Binary search with offset
    lo, hi = 0, n - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        real_mid = (mid + pivot) % n
        if nums[real_mid] == target:
            return real_mid
        elif nums[real_mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

print(search_via_pivot([4,5,6,7,0,1,2], 0))  # 4

Tout mettre en pratique

Lorsque vous rencontrez un problème de tableau ayant subi une rotation en entretien, suivez cet arbre de décision. Commencez par déterminer si vous devez trouver une cible ou trouver le minimum. Pour trouver une cible, utilisez l’approche d’identification de la moitié triée. Pour trouver le minimum, comparez mid à hi. Si des doublons sont possibles, mentionnez le pire cas O(n) et ajoutez une solution de repli consistant à resserrer les bornes.

Entraînez-vous en suivant l’exécution de votre code sur les trois exemples classiques : aucune rotation, une rotation, et une rotation plaçant le minimum à la dernière position.

Vérification rapide

Évaluez votre compréhension des concepts de structures de données et d’algorithmes — préparation aux entretiens de programmation abordés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que : un tableau trié après rotation possède toujours au moins une moitié triée, il faut comparer arr[lo] à arr[mid] pour déterminer quelle moitié est triée avant de décider où chercher, et la recherche du minimum utilise arr[mid] par rapport à arr[hi] pour localiser le pivot de rotation. Ensuite, nous découvrirons les variantes de recherche binaire avec borne inférieure et borne supérieure.

Questions Fréquemment Posées

La leçon « Recherche binaire dans des tableaux pivotés et non triés » est-elle gratuite ?

Oui — le texte complet de « Recherche binaire dans des tableaux pivotés et non triés » 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 DSA Interview Prep, passe à CoddyKit PRO. Le cours DSA Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Recherche binaire dans des tableaux pivotés et non triés » ?

Résolvez search-in-rotated-sorted-array et find-minimum-in-rotated-array en déterminant quelle moitié est triée à chaque étape. Tu pratiques DSA 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 DSA Interview Prep ?

Aucune expérience préalable n'est requise. DSA 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 2 sur 4.

Combien de temps prend la leçon « Recherche binaire dans des tableaux pivotés et non triés » ?

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 DSA Interview Prep ?

Oui. Chaque leçon DSA 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

  1. Recherche binaire classique : gauche, droite, milieu
  2. Recherche binaire dans des tableaux pivotés et non triés
  3. Borne inférieure et borne supérieure
  4. Recherche binaire dans l’espace des réponses
← Retour à DSA Interview Prep