(I am assuming that album:singer & song:album are actually both 1-or-more:exactly-1 because your "one to many"s contradict your other descriptions.)
Re 2 tables being correct: You should not use all three. SingerSong is always equal to (SingerAlbum JOIN SongAlbum) PROJECTed on SINGER & SONG. Using all 3 is redundant in a way that requires the update of multiple tables when the two-table design would involve only one and that requires a multi-table constraint to enforce consistency between SingerSong and the others.
Re designing tables: Every table has a meaning/predicate and every present row & every absent row (that fits it) makes a statement/proposition. We choose sufficient meanings/predicates to be able to describe all situations that can arise. (See this re bases and this re queries and my other answers using "predicate".) "Redundancy" is when two rows make overlapping statements/propositions at the same time. Then we have to change multiple rows when in another design we could just change one. (Or more vs fewer.) We just have to be aware of what our chosen meanings/predicates say. Sadly most information modeling methods & products don't address meanings/predicates of relationships/relations even though they are the foundation of modeling & the source of the terms in Entity-Relationship and Relational Model. Normalization addresses certain redundancies that can be eliminated from a meaning/predicate by splitting it at an AND. (Hence replacing its associated table/relation by a JOIN of others).
Re needing 3 tables: Here you have predicates "singer SINGER made album ALBUM", "song SONG is on album ALBUM" and "singer SINGER sings song SONG". We can't directly express the third in terms of the previous two. But since the third predicate is implied by the first plus the second in your particular application, you don't need it. Ie "singer SINGER sings song SONG" in your application exactly when "for some ALBUM, singer SINGER made album ALBUM and song SONG is on album ALBUM". (Ie the predicate satisfied by the rows in (SingerAlbum JOIN songAlbum) PROJECTed on SINGER & SONG.) But if say multiple singers could be on an album not both singing all songs or if say there were songs sung but not on an album then the first two would not imply the third so you could neither express nor infer what rows are in the third from those in the others. (Notice how your adding "at least one"/"only one" complicated the predicates and constraints and led to a redundant design even though you thought you "simplified the modeling to focus the question on the main topic".
Re optimization: Always produce the most straightforward design first. You should not worry about storage and time tradeoffs until you have a lot more experience with design and querying to appreciate the relevant factors. And then you should use estimates and measurements to justify changes.