Nombre d’Îles en Python : Résolvez cette Question d’Entretien Populaire

Titre: Nombre d'Îles en Python : Résolvez cette Question d'Entretien Populaire

Introduction

Les entretiens techniques sont cruciaux pour décrocher un emploi dans le domaine technologique. Parmi les questions couramment posées, celle du « Nombre d’Îles » est proéminente. Elle consiste à évaluer votre compréhension des algorithmes de recherche et de manipulation de données structurées. L’objectif de cet article est de vous offrir une compréhension approfondie de ce problème et de vous guider vers une solution efficace en Python.

Comprendre le Problème du « Nombre d’Îles »

Description du problème

Le problème du « Nombre d’Îles » implique une grille 2D binaire, où chaque élément est soit ‘1’ (terre) soit ‘0’ (eau). Une île est constituée de cellules contiguës en ‘1’ connectées horizontalement ou verticalement. Votre tâche est de déterminer combien d’îles distinctes se trouvent dans la grille.

Exemple :

Grille = [
  ['1', '1', '0', '0', '0'],
  ['1', '1', '0', '1', '1'],
  ['0', '0', '0', '0', '0'],
  ['0', '0', '1', '1', '0']
]

Dans l’exemple ci-dessus, nous avons trois îles.

Exemple de scénario d’application

Imaginez que vous analysez une carte pour un jeu vidéo. Chaque ‘1’ représente une masse terrestre et chaque ‘0’ une étendue d’eau. Vous devez compter les masses terrestres isolées pour gérer les ressources ou définir des objectifs de jeu.

Stratégies de Résolution

Parcours en Profondeur (Depth-First Search – DFS)

Le DFS explore aussi loin que possible le long de chaque branche avant de reculer. Lorsqu’il détecte une ‘1’, il utilise la récursion pour marquer toutes les cellules connectées en ‘1’ comme visitées.

Pseudo-code DFS :

Pour chaque cellule dans la grille:
    Si cellule == '1':
        Incrémenter le compteur d'îles
        Appeler DFS pour marquer toutes les cellules adjacentes '1' comme visitées

Complexité :

  • Temps : O(m * n) où m et n sont les dimensions de la grille
  • Espace : O(m * n) en raison de la pile d’appels récursifs dans le pire cas

Parcours en Largeur (Breadth-First Search – BFS)

BFS explore tous les voisins d’un nœud avant de passer au niveau suivant. C’est utile pour visiter toutes les parties connectées d’une île.

Pseudo-code BFS :

Pour chaque cellule dans la grille:
    Si cellule == '1':
        Incrémenter le compteur d'îles
        Utiliser une file pour explorer chaque cellule '1' adjacente

Comparaison avec DFS : BFS utilise généralement une file d’attente et peut être plus efficace niveau mémoire en évitant la pile de récursion.

Algorithme Union-Find (Approche avancée)

L’algorithme Union-Find, ou Union-Fusion, est efficace pour identifier et fusionner des ensembles disjoints, parfait pour ce type de problème.

  1. Chaque cellule ‘1’ est un nœud distinct.
  2. Fusionner les nœuds adjacents pour former des ensembles, chaque ensemble représentant une île.

Avantages : Rapide pour détecter et fusionner les composants connectés.

Inconvénients : Plus complexe à implémenter que DFS/BFS et nécessite une compréhension solide des structures de données.

Implémentation en Python

Mise en œuvre de DFS

def numIslandsDFS(grid):
    if not grid:
        return 0

    def dfs(i, j):
        if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == '0':
            return
        grid[i][j] = '0'  # Marquer la cellule comme visitée
        dfs(i + 1, j)
        dfs(i - 1, j)
        dfs(i, j + 1)
        dfs(i, j - 1)

    count = 0
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if grid[i][j] == '1':
                count += 1
                dfs(i, j)
    return count

# Exemple de test
print(numIslandsDFS([
  ['1', '1', '0', '0', '0'],
  ['1', '1', '0', '1', '1'],
  ['0', '0', '0', '0', '0'],
  ['0', '0', '1', '1', '0']
]))  # Sortie: 3

Mise en œuvre de BFS

from collections import deque

def numIslandsBFS(grid):
    if not grid:
        return 0

    def bfs(i, j):
        queue = deque([(i, j)])
        while queue:
            x, y = queue.popleft()
            for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                nx, ny = x + dx, y + dy
                if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]) and grid[nx][ny] == '1':
                    grid[nx][ny] = '0'
                    queue.append((nx, ny))

    count = 0
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if grid[i][j] == '1':
                count += 1
                bfs(i, j)
    return count

# Exemple de test
print(numIslandsBFS([
  ['1', '1', '0', '0', '0'],
  ['1', '1', '0', '1', '1'],
  ['0', '0', '0', '0', '0'],
  ['0', '0', '1', '1', '0']
]))  # Sortie: 3

Mise en œuvre d’Union-Find

class UnionFind:
    def __init__(self, grid):
        self.parent = {}
        self.count = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.parent[(i, j)] = (i, j)
                    self.count += 1

    def find(self, i, j):
        if (i, j) != self.parent[(i, j)]:
            self.parent[(i, j)] = self.find(*self.parent[(i, j)])
        return self.parent[(i, j)]

    def union(self, x, y):
        rootX = self.find(*x)
        rootY = self.find(*y)
        if rootX != rootY:
            self.parent[rootY] = rootX
            self.count -= 1

def numIslandsUnionFind(grid):
    if not grid:
        return 0

    uf = UnionFind(grid)

    directions = [(1, 0), (0, 1)]
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if grid[i][j] == '1':
                grid[i][j] = '0'
                for d in directions:
                    ni, nj = i + d[0], j + d[1]
                    if 0 <= ni < len(grid) and 0 <= nj < len(grid[0]) and grid[ni][nj] == '1':
                        uf.union((i, j), (ni, nj))

    return uf.count

# Exemple de test
print(numIslandsUnionFind([
  ['1', '1', '0', '0', '0'],
  ['1', '1', '0', '1', '1'],
  ['0', '0', '0', '0', '0'],
  ['0', '0', '1', '1', '0']
]))  # Sortie: 3

Optimisation et Meilleures Pratiques

Considérations sur la complexité

Pour des grilles de grande taille, la complexité spatiale et temporelle des solutions doit être soigneusement examinée. Le DFS et le BFS ont des complexités similaires, mais l’utilisation de la mémoire peut être un facteur clé dans le choix de l’approche.

Conseils pour les entretiens

  • Expliquer la Solution : Soyez clair et méthodique. Décrivez votre approche étape par étape.
  • Meilleures Pratiques : Écrivez un code propre, utilisez des commentaires pour décrire les parties complexes et restez calme sous pression.

Exemples Pratiques

Résolution de plusieurs scénarios types

Testez vos solutions avec des grilles de tailles variées pour bien comprendre les limites de performances et l’efficacité de chaque algorithme.

Tester vos compétences avec des exercices

  • Testez des configurations de grilles non standards pour affiner votre compréhension du problème.
  • Essayez de résumer votre solution de manière concise.

Conclusion

Nous avons exploré plusieurs méthodes pour résoudre le problème du « Nombre d’Îles » en Python, notamment DFS, BFS, et Union-Find. Chaque méthode a ses avantages et ses inconvénients, mais il est crucial de les comprendre pour réussir lors des interviews techniques. Pratiquez ces concepts avec différentes implémentations pour renforcer vos compétences.

Ressources et Lectures Complémentaires

  • Articles détaillés sur DFS, BFS, et Union-Find.
  • Livres recommandés : Introduction to Algorithms par Cormen et Algorithm Design Manual par Skiena.
  • Plateformes d’entraînement : LeetCode, HackerRank.

FAQs

Quelle est l’approche la plus efficace pour une grille très dense en ‘1’ ?

DFS est souvent plus rapide dans des configurations denses, mais BFS peut être plus sûr si la profondeur de récursion est un souci.

Quels sont les meilleurs conseils pour aborder d’autres questions d’entretien similaires ?

Comprenez les fondements de chaque algorithme, préparez-vous à discuter de la complexité et pratiquez la mise en œuvre rapide et précise.