There are three relevant differences between the operators &&/|| and &/|, which are explained in the official documentation. Here’s a summary:
1. & and | are vectorised
This means that if you want to perform element-wise logical operations on vectors you should use & and |:
a = c(TRUE, TRUE, FALSE, FALSE)
b = c(TRUE, FALSE, TRUE, FALSE)
a | b
# [1]  TRUE  TRUE  TRUE FALSE
a || b
# [1] TRUE
For &&/|| all elements after the first are discarded. Recent versions of R generate a helpful warning when using &&/|| on vectors longer than 1:
In a || b : 'length(x) = 4 > 1' in coercion to 'logical(1)'
2. && and || are short-circuited
Short-circuiting means that the right-hand side of the expression is only evaluated if the left-hand side does not already determine the outcome. Pretty much every programming language does this for conditional operations, since it leads to handy idioms when writing if conditions, e.g.:
if (length(x) > 0L && x[1L] == 42) …
This code relies on short-circuiting: without it, the code would fail if x is empty, since the right-hand side attempts to access a non-existent element. Without short-circuiting, we would have to use nested if blocks, leading to more verbose code:
if (length(x) > 0L) {
    if (x[1L] == 42) …
}
As a general rule, inside a conditional expression (if, while) you should always use && and ||, even if short-circuiting isn’t required: it’s more idiomatic, and leads to more uniform code.
3. & and | can perform bitwise arithmetic
In many (most?) programming languages, & and | actually perform bitwise arithmetic instead of Boolean arithmetic. That is, for two integers a and b, a & b calculates the bitwise and, and a | b calculates the bitwise or. For Boolean values there’s no difference between bitwise and logical operations; but for arbitrary integers, the result differs. For instance, 1 | 2 == 3 in most programming languages.
However, this is not true for R: R coerces numeric arguments of & and | to logical values and performs Boolean arithmetic.
… except when both arguments are of type raw:
c(1, 3) | c(2, 4)
# [1] TRUE TRUE
as.raw(c(1, 3)) | as.raw(c(2, 4))
# [1] 03 07
It is worth noting that the operations ! (logical negation) and xor also perform bitwise arithmetic when called with raw arguments.