Skip to content
HomeVideosBooks
AllTopicsTrends
BlogsPodcasts
HistoryListsWatch laterLiked videosFollowing

The videos, books, blogs and podcasts Hacker News keeps recommending, all in one place.

VideosBooksBlogsPodcastsAboutPrivacySettings

Data from Hacker News, HackerTube, HackerBooks, HackerBlogs and Open Library. Credits · Not affiliated with Y Combinator.

HomeVideosBooksBlogsPodcasts
Ctrl K
    Cover of Algorithms in C++

    Algorithms in C++

    Robert Sedgewick · 1990

    Robert Sedgewick has thoroughly rewritten and substantially expanded his popular work to provide current and comprehensive coverage of important algorithms and data structures. Many new algorithms are presented, and the explanations of each algorithm are much more detailed than in previous editions. A new text design and detailed, innovative figures, with accompanying commentary, greatly enhance the presentation. The third edition retains the successful blend of theory and practice that has made Sedgewick's work an invaluable resource for more than 250,000 programmers! Whether you are a student learning the algorithms for the first time or a professional interested in having up-to-date reference material, you will find a wealth of useful information in this book.

    Computer ScienceProgramming
    Open LibraryAmazon
    Stats for nerds
    27recommend
    45mentions
    37people
    2009–2025years
    76% recommend20% neutral4% critical

    Mentions per year

    200920112013201520172019202120232025

    What Hacker News says

    1. kragenOct 2025
      Sedgewick published a C++ version of his book using templates for parametric polymorphism, but C++ is kind of a nightmare of a language. Unfortunately algorithms like quicksort really seem to demand parametric polymorphism, and the alternative languages with parametric polymorphism such as Haskell and ML mostly make memory allocation implicit, which runs counter to your goals. Rust offers both explicit memory management and parametric polymorphism, but Rust code is not very clean. Digital Mars D might be a possible alternative? But none of Rust, D, Pony, Zig, Nim, Odin, or even C++ are as widely understood as C, Python, and JS.

      Sedgewick's C edition is the book that first showed me that programs can be beautiful. His favorite algorithm is of course Quicksort. His version of the partition function from Algorithms in C (01998 edition) is, I think, worth studying:

          int partition(Item a[], int l, int r)
           { int i = l-1, j = r; Item v = a[r];
             for (;;)
               {
                 while (less(a[++i], v)) ;
                 while (less(v, a[--j])) if (j == l) break;
                 if (i >= j) break;
                 exch(a[i], a[j]);
               }
             exch(a[i], a[r]);
             return i;
           }
      
      There are some things to dislike in this presentation, like naming a variable "l", the rather excessively horizontal layout, and the brace style. And of course he picked Hoare's original branchy partition algorithm rather than Lomuto's simpler one. But I think there are some other choices that are beneficial:

      • He's using short variable names to let the algorithm speak for itself.

      • He uses a type Item (perhaps a typedef) to make the distinction clear between the type of array elements and the type of array indices.

      • Similarly, he's using exch and less functions to abstract away the comparison and exchange operations. In the Golang standard library implementation, these are actually methods on the container to be sorted taking indices as arguments, so the concrete item type doesn't appear in the algorithm at all, which I think is an important insight. Of course the numerous

      It would be possible to use the C preprocessor to compile specialized versions of the function for particular data types and comparators in a type-safe way by redefining Item, exch, and less, but of course when you do that the error messages are difficult to decipher. (But exch can't be just a regular function, because C passes arguments by value.)

      Sedgewick's original presentation in the book (from the 01983 edition) inlines the partitioning into the Quicksort loop:

          procedure quicksort(l, r: integer)
            var v, t, i, j: integer;
            begin
            if r > l then
              begin
              v := a[r]; i := l-1; j := r;
              repeat
                repeat i := i+1 until a[i] >= v;
                repeat j := j-1 until a[j] <= v;
                t := a[i]; a[i] := a[j]; a[r] := t;
              until j <= i;
              a[j] := a[i]; a[i] := a[r]; a[r] := t;
              quicksort(l, i-l);
              quicksort(i+1, r)
              end
            end;
      
      From my perspective the C edition is an enormous improvement. I don't have a copy of the (Pascal) second edition to compare.

      The third C++ edition (01998) looks almost identical to the C version, except that it's templatized on the Item type, which in C++ also permits you to use static ad-hoc polymorphism (funciton overloading) for the exch function and the comparison operator:

         template <class Item>
         int partition(Item a[], int l, int r)
           { int i = l-1, j = r; Item v = a[r];
             for (;;)
               {
                 while (a[++i] < v) ;
                 while (v < a[--j]) if (j == l) break;
                 if (i >= j) break;
                 exch(a[i], a[j]);
               }
               exch(a[i], a[r]);
               return i;
            }
      
      He illustrates the dynamic behavior of sorting algorithms in multiple ways. One is a vertical time sequence of horizontal arrays of letters, circling the pointed-to elements and graying out the unchanged ones. Another, which can scale to larger arrays, is showing a small-multiple time sequence of scatterplots, like a comic strip, where the X coordinate is the array index and the Y coordinate is the element value at that array index. (A variant of this draws bars whose heights are the element values.) From my perspective, static visualizations like this should be preferred over animation when applicable (https://worrydream.com/MagicInk/), but this case is maybe on the borderline where both animation and static visualization have unique merits.

      My own terse presentation of Lomuto's algorithm is http://canonical.org/~kragen/sw/dev3/paperalgo#addtoc_21. I don't think it reaches the clarity of an exposition in C or Python, but it is much easier to write on a whiteboard. I do think that expressing the partition subroutine in terms of an operation on a Golang-like slice improves the clarity significantly. In C or C++ you could pass a base pointer and a length; perhaps Sedgewick didn't want to expose students to this aspect of C arrays, or perhaps he was still constrained by Pascal thinking.

      thread
    2. t312227Sep 2023
      hello,

      2nd vote for ... kernighan, ritchie "the c programming language"

      and after you master the basics maybe robert sedgewick "algorithms in c"

      and: pointers ... especially if you are coming from scripting-languages or c#/java you probably only have a weak concept of reference vs value for variables and pointers for other fundamental building-blocks (c function pointers anyone ... ;)

      so after the introduction look especially at pointers, because its a "new concept" and its very essential in c ;)

      a more extensive list is for there: * https://stackoverflow.com/questions/562303/the-definitive-c-...

      just my 0.02€

      thread
    3. kragenJul 2022
      Here's a few fun ones: the Burrows–Wheeler transform, suffix arrays in general, Kanerva's fully distributed representation, reduced-affine-arithmetic numbers, LSM trees, binary array sets, sparse array sets, and gap buffers. (I'm not sure how nobody had mentioned gap buffers yet on the thread, but if they did I didn't see it.)

      — ⁂ —

      The Burrows-Wheeler transform is the second character of each suffix in a (cyclic) suffix array, and there turns out to be a simple algorithm to recover the original text from this transform; live demo: http://canonical.org/~kragen/sw/bwt. But the new text is very easily compressible, which is the basis for bzip2. As mentioned by Blahah and dahart, this is only one of many interesting things you can do with suffix arrays; they can also do efficient regular expression search, for example. This is more interesting since three practical linear-time and -space suffix array construction algorithms were discovered in the early 02000s.

      — ⁂ —

      Pentti Kanerva devised a "fully distributed representation" in the 01990s, which has some features in common with bloom filters — every association stored in an FDR record is stored to an equal extent (probabilistically) in every bit of the record, so erasing some of the bits erases none of the associations but increases the error probability for all of them. You assign a random or pseudorandom bitvector (of, say, 16384 bits) to every atomic value to be stored, represent an association of two (or more) atoms as their XOR, and then take the bitwise majority rule of all the associations to be stored as the record. To retrieve an association, you XOR the record with an atom, then compute the correlation of the XOR result with all possible atoms; the correlation with the correct atoms will be many standard deviations away from the mean, unless the number of associations stored in the record is high enough that conventional data structures wouldn't have been able to store it either.

      This and some related work has blossomed into a field called "hyperdimensional computing" and "vector symbolic architectures". But the problem of finding the nearest atom or atoms seems, on its face, to make this impractical: if you have 16384 possible atoms and 16384 bits in your bitvector, you would seem to need at least 16777216 bit operations to compute all the associations.

      I realized the other night that if you use the rows of a Walsh–Hadamard matrix as the "pseudorandom" atom representations, you can compute all the correlations in linearithmic time with the fast Walsh–Hadamard transform. Moreover, Cohn and Lempel's result from 01977 "on fast M-sequence transforms" is that after a simple transposition you can also use the FWHT to compute all the correlations with the complete output of a maximal LFSR (a so-called "M-sequence") in linearithmic time, so you can also use the cyclic shifts of an LFSR output for your atoms without losing efficiency. Probably someone else has already figured this out previously, but I hadn't found it in the literature.

      — ⁂ —

      I'm not sure if the quantities used in reduced affine arithmetic count as a "data structure"? They're a data type, at least, but they're not a container. The idea is that you execute some arbitrary numerical algorithm, not merely on floating-point numbers, or even on dual numbers as in forward-mode automatic differentiation, nor even yet on (min, max) intervals as in ordinary interval arithmetic, but on a vector of coefficients [a₀, a₁, a₂...aₙ], which represents a linear function a₀x₀ + a₁x₁ + a₂x₂ + ... aₙxₙ, where x₀ = 1 and each of the other xᵢ ∈ [-1, 1], so this function is really a representation of an interval. Then you can assign each of the xᵢ (except x₀ and xₙ) to be the unknown error in one of the input parameters to your algorithm, whose magnitude is determined by the aᵢ value in the value of that parameter; and when you introduce rounding error, you increase aₙ enough to account for it. (The non-reduced kind of affine arithmetic just increases n without limit to account for new roundoff errors.)

      Reduced affine arithmetic has two advantages over the usual kind of interval arithmetic. First, it cancels errors correctly; if you calculate (y+1)(y+2)/(y+3) with interval arithmetic, with some uncertainty in the value of y, you usually get an unnecessarily loose bound on the result because interval arithmetic assumes that the uncertainties in the numerator and the denominator are uncorrelated, when in fact for many values of y they partly cancel out. (You can't apply this cancelation to aₙ, the error stemming from all the roundoffs in your algorithm, just the other aᵢ.) But the more interesting result is that RAA gives you a linear function that approximates the calculation result as a linear function of the inputs, which is pretty similar to calculating the gradient — but with error bounds on the approximation!

      — ⁂ —

      LSM trees are only about as "obscure" as bloom filters, since they underlie several prominent filesystems, LevelDB, and most of the world's search engines, but if you don't know about bloom filters, you might not know about LSM trees either. I wrote a tutorial explanation of LSM trees in https://github.com/kragen/500lines/tree/master/search-engine, though the name "LSM tree" hadn't yet gone mainstream.

      Binary array sets, as described in https://www.nayuki.io/page/binary-array-set, are closely related to LSM trees, to the point that you could describe them as an application of LSM trees. They are a simple data structure for the exact version of the problem bloom filters solve approximately: maintaining a set S of items supporting the operations S.add(item) and S.contains?(item). Insertion (.add) is amortized constant time, and membership testing (.contains?) is O(log² N).

      — ⁂ —

      A different and more efficient data structure for the exact set membership problem, limited to the case where the items to be stored are up to M integers in the range [0, N), supports constant-time creation, clearing, insertion, size measurement, and membership testing, and linear-time enumeration, but I think cannot be implemented in modern standard C because of its rules about reading uninitialized data. (By allowing creation to be linear time you can implement it in standard C.) It uses two arrays, A[M] and B[N] and a variable n, and i is a member of the set iff B[i] < n ∧ A[B[i]] == i, from which it is straightforward to work out the other methods.

      I don't remember what this data structure is called, but I definitely didn't invent it. I described it and some uses for it in https://dercuano.github.io/notes/constant-time-sets-for-pixe.... You can associate additional values with this structure to use it as a sparse array.

      Oh, I see kcbanner posted this one too, with a link to Russ Cox's writeup, which is better than mine and explains that it was published by Briggs and Torczon in 01993, but probably not originally invented because it was an exercise in an Aho, Hopcroft, and Ullman textbook in 01974 and in Programming Pearls: https://research.swtch.com/sparse

      — ⁂ —

      A gap buffer is a data structure sort of similar to Huet's zipper but without the dynamic allocation; Emacs famously uses them for its editing buffers. It consists of an array containing of a leading chunk of text, followed by a gap, followed by a trailing chunk of text. Edits are always made in the gap; to start editing in a different position, you have to move some text from the end of the leading chunk to the beginning of the trailing chunk or vice versa.

      Suppose you want to edit a 531-byte file; you allocate a larger buffer, say 1024 bytes, and read those 531 bytes into the beginning of the buffer. If the user starts inserting text after byte 200, you begin by moving bytes 201–530 (330 bytes) to the end of the 1024-byte buffer (positions 694–1024); then you can insert the user's text at positions 201, 202, 203, etc., without having to do any more work to move text. If the user moves up to byte 190 and inserts more text, you only have to move bytes 191–203 or whatever to the end of the buffer.

      A gap buffer is very fast in the usual case of making a sequence of small edits at the same location or close-together locations, and because the constant factor of copying a large block of bytes from one place to another is so small, even moving the gap from one end of a buffer to another is generally plenty fast enough for interactive use. Moreover, operations like string search are very fast in a gap buffer, while they tend to be very slow in fancier data structures with better asymptotic complexity, such as piece tables and ropes; and it uses a predictable and relatively small amount of extra memory.

      — ⁂ —

      However, like FORTH, fancy data structures are a bit of an attractive nuisance: they tempt you to get into trouble. When I first came across Sedgewick's Algorithms in C it was a revelation to me: here were so many clever ways of breaking down problems that made apparently impossible problems easy. (I had come across sorting algorithms before but I didn't really understand them.) I immediately and erroneously concluded that this was the knowledge I had been missing to program all the things that I didn't know how to program, and I began my counterproductive data-structure-fetishism phase.

      Much better advice is found in The Practice of Programming: almost all programs can be written without any data structures but arrays, hash tables, linked lists, and, for things like parsing, symbolic algebra, or filesystems, trees. So just use those if you can.

      Occasionally, a program is only possible, or is dramatically better, using fancy data structures like LSM-trees, bloom filters, suffix arrays, skip lists, HAMTs, union find, zippers, treaps, BDDs, ropes, gap buffers, Fenwick trees, splay trees, and so on. And that can be super fun! But this situation is pretty rare, and it's important not to get yourself into the position where you're implementing a red-black tree or HAMTs when it would have been a lot easier to write your program in a brute-force way. Or where your successors are fixing bugs in your Fenwick tree implementation.

      Also, it's important to be aware that, quite aside from the difficulty of writing and understanding the code, fancy data structures often have extra costs at runtime which can outweigh their benefits. It's easy to discover that your splay tree runs slower than sequential search over an unsorted array, for example, and of course it uses much more RAM.

      thread
    4. mindcrimeDec 2021
      I'm a big fan of Sedgewick's books myself. I like how he has parallel series of his books, for different programming languages. So you get "Algorithms in Java" or "Algorithms in C++" or whatever, depending on your preference. I think he has at least C, C++, and Java versions.

      https://www.cs.princeton.edu/~rs/

      thread
    5. morty_sNov 2021
      CLRS does about 5 pages on linked lists. If you want more, my advice would be to implement one in your language of choice, then look at the API for the standard lib impl of a linked list in your language of choice (if there is one).

      Otherwise, for more reading on linked lists, read through adlist.c from redis, by antirez.

      Next, one of the best books (IMO) on algorithmic thinking is SICP. Get through a few chapters, do the exercises, grok recursion, etc.

      In no particular order:

      Algorithm Design Manual, Skiena

      Algorithm Design, by Kleinberg/Tardos

      Algorithms, Sedgwick 4th ed. (also Algorithms in C)

      If you find you need or would like more math:

      Discrete Mathematics (Epp is an easier read, Rosen seems more verbose)

      Mathematical Proofs, Charrand

      For interview prep:

      Elements of Programming Interviews (EPI)

      Designing Data Intensive Applications

      thread
    6. SavantIdiotOct 2021
      Similarly, Robert Sedgewick covered this in his 1990's book "Algorithms in C" quite nicely, and a bit more succinctly, IMO.

      https://www.amazon.com/Algorithms-Computer-Science-Robert-Se...

      thread

    Related

    Cover of Algorithms
    2007–2026

    Algorithms

    Robert Sedgewick
    50 recommend · 100
    Numerical AlgorithmsJustin Solomon
    2016–2025

    Numerical Algorithms

    Justin Solomon
    1 recommend · 2
    The C++ standard template libraryP. J. Plauger
    2015–2026

    The C++ standard template library

    P. J. Plauger
    1 recommend · 8
    Cover of The C++ programming language
    2008–2026

    The C++ programming language

    Bjarne Stroustrup
    19 recommend · 49
    Cover of Discovering Modern C++
    2016–2026

    Discovering Modern C++

    Peter Gottschling
    4 recommend · 8
    Cover of Introduction to Algorithms
    2007–2026

    Introduction to Algorithms

    Thomas H. Cormen
    274 recommend · 880
    Cover of The algorithm design manual
    2007–2026

    The algorithm design manual

    Steven S. Skiena
    recommend · 321
    195
    Cover of Ruminations on C++
    2016–2025

    Ruminations on C++

    Andrew Koenig
    3 recommend · 4