For any given position on the board, there are only 9 possible moves that can be made (1,2,3,4,5,6,7,8,9). One could try each possible move without regard to the rules of Sudoku, then evaluate whether the new state is correct - and this is what I understand you mean by brute force.
My implementation goes to the next step and only considers valid moves for a given position by looking within the current 3x3 square, and also by looking along the row+column to eliminate numbers that have already been used. This is slightly better than brute force and reduces the search space quite a bit.
At a high level, my sudoku solver does a guided DFS by generating the full list of moves that can be made from the current board state, then sorts them according to heuristics that match how I play sudoku (first try to fill in positions with less options, first try to use numbers that have been used more), then starts trying values and backtracking once it reaches a point where there are no more remaining valid moves.
The problem with heuristics like those is that sudoku puzzles can be created that exploit the heuristics and make the search take forever. My implementation solves easy and medium sudokus very quickly. My implementation can't solve most hard sudokus even after letting it run for hours.