Programming involves a particular flavour of problem solving. If you're a beginner trying to get better at it, an educator teaching beginners, or just someone who enjoys thinking about thinking, then I may have just the book for you: Think Like a Programmer from No Starch Press.
This book is, as its subtitle promises, an introduction to creative problem solving. It starts with a chapter on general problem solving techniques such as breaking problems into smaller pieces and looking for answers in what you already know. It then devotes a chapter to solving non-programming problems with code. After that, most of the book covers some of the basic problem types all programmers encounter, including arrays, dynamic memory, class design, recursion, and code reuse. The last chapter summarizes general techniques for thinking like a programmer.
I read this book as someone interested in computer science education, particularly for beginners. But despite the fact that I'm an experienced programmer of ten years, I actually enjoyed reading about things I already know how to do. It ended up being an interesting exercise in thinking about thinking, and it brought my attention back to some of the details about programming that I now take for granted.
The author has an easy-to-understand, approachable, discussion-based style of writing. There is a good progression through topics — as a course instructor, you could certainly mirror the flow of the book even if you didn't want to use it as the official text. The numerous diagrams are clear and genuinely useful. I also love that good programming practices are embedded in the discussion, and that many other aspects of CS (for example, data structures) make an appearance in a natural way.
One of the chapters I was most curious about is the one on recursion, given how difficult this topic can be to teach. I liked that this topic was introduced with a detailed real-life example of head and tail recursion. I'm not sure if I was never explicitly taught these concepts or if I've come to take them for granted by now, but I have at the very least not thought about the difference in a long time. I appreciated bringing these ideas back to my conscious mind, and felt like the discussion should be helpful to beginners. I also liked the suggestion to solve problems as if there was no recursion, and to just trust the recursive call to do its thing. (Incidentally, you can read this chapter online, where it is offered as a sample.)
There were a few things I didn't like as much, such as the unnecessarily long paragraphs. I also thought some of the sample problems were cliched and rather dull (how many of us actually care about keeping track of student records?). Each chapter begins with a very fast review of relevant C++ syntax, and while I understand why this is done, I find it ends up being too sparse to be useful to anyone who actually needs it.
Probably the thing I struggle with the most is the choice of C++. I actually think C++ is a reasonable early language for computer science majors (at least in terms of what's currently available), and the rationale for choosing it for the book is sound. However, I don't really know of many post-secondary programs that teach C++ first. The problem is that the book would become a lot less relevant after the first course or two in a good CS program since the concepts will already have been covered, but it's difficult to make use of it unless you already know some C++.
Nonetheless, given to a new programmer at the right time, I think the book has the potential to be very valuable. It comes highly recommended in my mind.