std re-exports everything that core and alloc contain (but there's also things in std that onlystd contains.) Unless you're writing a no_std crate, you only need to worry about & use std. The “similar” items are actually exactly the same items, there's no difference.
::core features the OS-agnostic abstractions (which includes atomics when they are available)
::alloc then adds the abstractions that are OS-agnostic but for assuming there is an allocator available, i.e., that "there is a heap"; such as Box, Vec, String, {A,}Rc.
::std then adds everything else, which includes:
I/O (::std::io module),
high-level synchronization primitives such as Mutex and channels.
By default, a crate has access to ::std, and thus, everything, by using ::std paths. ::core is still also available in the prelude, so if you fancy it, you could use ::core-based paths to refer to OS-agnostic abstractions, such as ::core::mem::swap. In practice, there are only two reasons to do that:
when within a macro which wants to be usable in places where ::std is not available;
I personally do that when providing code examples, to get people used to knowing what may and what may not be available in ::core, to make a future transition to #![no_std] environments easier
But, as I just mentioned, there are environments where ::std is not available, such as "bare-metal" programming, where no OS is available. This happens very often when doing embedded programming. In those cases, one has to deal with #![no_std]-annotated crates, whereby making ::std unaccessible (unless one were to opt back into std through an extern crate std, but in that case they'd no longer have the std prelude)
So, such crates, by default, only have access to ::core.
They can, however, embed a middle-ground crate, such as ::alloc, when their embedded environment allows it, by adding an extern crate alloc; declaration.