Skip to content

Broad First Search

Sliding Puzzle (Leetcode 773)

We can think of this problem as a graph traversal problem, where each state is a configuration of the puzzle, and the neighbors of a state are the configurations that can be reached by sliding a tile.

from collections import deque

class Solution:
    def slidingPuzzle(self, board: List[List[int]]) -> int:
        # Decompose the 2D list into 1D string

        start_state = "".join(str(x) for row in board for x in row)
        target = "123450"

        neighbor_map = {
            0: [1, 3],
            1: [0, 2, 4],
            2: [1, 5],
            3: [0, 4],
            4: [1, 3, 5],
            5: [2, 4]
        }

        queue = deque()
        queue.append((start_state, 0))
        visited = set()
        visited.add(start_state)

        while queue:
            state, step = queue.popleft()

            if state == target:
                return step

            # Let us find the zero index
            zero_idx = state.index('0')

            for neighbor_idx in neighbor_map[zero_idx]:
                state_list = list(state)

                state_list[zero_idx], state_list[neighbor_idx] = state_list[neighbor_idx], state_list[zero_idx]

                new_state = "".join(state_list)

                if new_state not in visited:
                    visited.add(new_state)

                    queue.append((new_state, step+1))

        return -1

Open the Lock (Leetcode 752)

from collections import deque
class Solution:
    def openLock(self, deadends: List[str], target: str) -> int:
        start = '0000'

        if target == start:
            return 0

        def getNeighbors(state):
            res = []
            for i in range(4):
                x = int(state[i])
                for d in (-1, 1):
                    y = (x + d) % 10 
                    new_state = state[:i] + str(y) + state[i+1:]
                    res.append(new_state)
            return res

        visited = set(deadends)
        queue = deque()

        if start in visited:
            return -1



        queue.append(('0000', 0))
        visited.add('0000')


        while queue:
            state, step = queue.popleft()

            if state == target:
                return step

            for neighbor in getNeighbors(state):
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append((neighbor, step + 1))

        return -1