1.)
((i <= n) && (a[i] == 0)) || (((i >= n) && (a[i-1] == 0)))
The expression will be true IF the first part is true, or if the first part is false and the second part is true. This is because || uses "short circuit" evaluation. If the first term is true, then the second term is *never even evaluated*.
For || the expression is true if *either* part is true, and for && the expression is true only if *both* parts are true.
a.) (i <= n) || (i >= n)
This means that either, or both, of these terms is true. This isn't sufficient to make the original term true.
b.) (a[i] == 0) && (a[i-1] == 0)
This means that both of these terms are true. We substitute.
((i <= n) && true) || (((i >= n) && true))
Remember that && is true only if both parts are true. So if you have x && true, then the truth depends entirely on x. Thus x && true is the same as just x. The above predicate reduces to:
(i <= n) || (i >= n)
This is clearly always true.