Well, going through your code I found a few things that are a little confusing so not going to explain your exact code and what it does, but here is the basics…
The beauty of recursion is that you just handle a little piece of the problem, then recall the function to handle the rest of the problem.
So in the Sudoku solver, this is done by calling your solve function recursively… It’s a pretty messy thing to explain in english, but basically the steps look like this:

Find an empty cell (if all cells are filled in, your puzzle is solved)

Assign 1 to that cell if fits there (doesn’t conflict with other already completed cells)
(If 1 doesn’t work, then try 2, etc until you
find a workable number for that empty cell…
if you can’t find a workable number, then it returns false)

Now, call solve on the grid again with that empty cell now filled in
(This will start at step 1 again with whats left of the puzzle…
it repeats this until it either fills in every blank in the grid, or
it gets to a point where it finds a blank spot where 19 doesn’t
fit in the cell meaning it can’t solve the puzzle)

That call to solve in step 3 will either return a completed puzzle, in which case you’ve solved it, or will return false in which case you try the next workable number from step 2

This will continue until you’ve found a solved puzzle, or you’ve tried every number, and return false as not solved.
Its a very bruteforce attack, trying many different possible combinations until it finds one that works… to illustrate this maybe more clearly lets look at a simpler example such as a combination solver.
Say we want a program that will find the correct 3 digit combination to a safe… say the combination is 352… and you can’t repeat the same number twice, like 111, and can’t use 0… we’ll use 0 to represent an empty spot like in your Sudoku.
So using the same kind of solver as illustrated above we will follow the steps:
 call solve on 000
 pick the first empty spot, and assign it 1 if it fits, that gives us 100
 call solve on 100 (the recursion)
 pick the first empty spot (which would be the second number) and assign it 1… oh wait, that conflicts because we can’t repeat numbers, so assign it 2, for 120
 call solve on 120
 pick the first empty spot and assign it 1… oh wait, 2… oh wait 3… now 123…
 call solve on 123
 there are no empty slots left… but this was an incorrect answer, return false
 that takes us back to the solver called on 120 (the backtracking)… and since putting a 3 in the empty slot didn’t work, try the next number, so 124
 call solve on 124
 there are no empty slots left… but this was an incorrect answer, return false
 try 125… nope…
 try 126… nope…
This will continue up to 129… at which point its tried all workable numbers and didn’t solve the puzzle. It will return false for incorrect answer, and return false since it tried every number unsuccessfully, backtracking us back to where you called solve on 100, and will try the next number in the next empty slot, so 130. Solve on 130 will keep going until it tried every number up to 139… and when that doesn’t work, it will go to 140. When it gets to 198 and still doesn’t have an answer, its tried every number in the second and third slots unsuccessfully, so now will return false all the way back to the solve you called on 000… at which point since solve 100 didn’t work… it will try solve 200. This will continue in this fashion until it gets to 352 at which point the since it found a correct answer, it will return the answer(instead of false), which will return the answer, which will return the answer all the way back to your original call of solve (this is why a call to solve either returns false, or the completed puzzle).
Very bruteforce, but if there is a valid answer, it will find it. In this example it checked if the combination was right after there are no remaining slots, but in your Sudoku solver, it will backtrack anytime it gets to an empty square that it can’t assign a 19 because it conflicts with other cells already assigned.
It’s actually fun to watch this method at work… in your Sudoku solver, you can watch how it works by printing your grid string to console every time you assign a number to the grid (warning: this will print thousands of lines to your console). You’ll be able to watch as your program fills in the grid, usually almost completely, before it hits a conflict and has to backtrack sometimes almost all the way to the beginning to try the next number. Very interesting… or maybe I’m just easily entertained.
I hope this helps a bit. It’s late, so this might just be incoherant babbling.