What are the lesser known but cool data structures?
stackoverflow.com
stackoverflow.com
Judy arrays came up once too; there's a really excellent critique of Judy arrays here:
http://www.nothings.org/computer/judy/
(Long story short: you can get comparable performance from a straightforward hash table, and Judy Arrays have to be tuned to the microarchitecture).
Favorite data structure not cited here:
Aguri trees, which marry a bounded-size radix trie (like you'd use in a software routing table) to an LRU list, and automatically synthesize aggregates (like, 10.0.0.0/16 from 1,000 observations across all IPs) from the pattern of insertion. They're best known in traffic analysis, but we've used them on in runtime memory analysis as well.
Not just probably, absolutely. Skip lists are probably pretty balanced, which is always less balanced than actually pretty balanced. Their sole claim to fame is their simplicity in implementation compared to actual self-balancing trees.
http://www.cl.cam.ac.uk/research/srg/netos/lock-free/
(That's a lock-free red-black tree impl).
First, there's the approach everybody remembers from Intro to Algorithms: use a binary min-heap. It's guaranteed to be balanced, so you get O(lg n) time insertions and delete-the-minimum operations, with low constant factors. Nice! But how do you make it concurrent? You can put a lock on the whole thing and only let one thread use it at once, but that's slow. You could use fancy fine-grained locking, but there will still be inter-thread memory conflicts arising from the heapify operations you need to maintain the heap invariant. There has been some work on this, and they've come up with some decent ideas, but it still has scaling problems.
Now look at skip lists. It's a randomized sorted list data structure, and it claims to be, as you put it, probably pretty balanced. Various threads can insert concurrently without breaking that "probably pretty balanced" property. The memory read- and write-sets are very local, and it's possible to do all this with lock-free synchronization. The end result is a priority queue data structure that scales to hundreds of cores. And the code doesn't fry your brain, which is a plus. There's a pretty neat paper about it here:
http://www-cs-students.stanford.edu/~itayl/ipdps.pdf
By the way, if you happen to be using a processor with hardware transactional memory support (you aren't, yet), then this code becomes even easier to write, as you don't have to worry about how to do lock-free synchronization. I almost felt cheated by how simple it was.
That said, future HTM systems have a lot of potential, and I think we'll see them come into wider use eventually. The main problem with them seems to be that existing software ecosystems aren't written with HTM in mind, so you get pathological memory access patterns slowing things down. But with some minor enhancements to the HTM design (like a load instruction which immediately adds its destination address to the write set of a transaction) and compiler and runtime support, and a reasonable set of concurrent data structures, HTM can absolutely fly.
But I can't see why the same cannot be done using trees. :-/
http://en.wikipedia.org/wiki/Unrolled_linked_list
The idea is the same as a linked list, but instead of just one element in each node, it stores an entire array. This simple change fixes the two biggest problems with linked lists — memory overhead and cache efficiency. It's also easy to tweak to be more "array-like" or more "list-like" as needed.
http://www.python.org/dev/peps/pep-3128/
The BList is similar to a B+Tree. It was meant to act like an array for small lists, but to make operations like append, concatenation, and slicing faster. It was a pretty cool combination of arrays (for memory compactness and good cache performance and small constant factors) with trees, for the asymptotic improvements they can bring.
Sadly, it didn't take off, as it would have broken backward compatibility with extension modules.
(I don't know offhand if any widely used ones currently do, though.)
However, it's possible that clojure uses this layout for arrays and a different ones for lists or other sequences.
http://blog.higher-order.net/2009/02/01/understanding-clojur...
This allows for tremendous compression of search spaces -- one example Knuth gave in a talk I went to a few weeks ago was representing all five-letter words in the English (represented as subsets of {a_1, a_2, ..., z_4, z_5}, where e.g.: {k_1, n_2, u_3, t_4, h_5} represents "knuth"), and efficiently making queries such as finding all words that, when a 'b' is replaced with an 'o', yield another word. More impressive to me was representing all of a certain class of tilings with only a few hundred thousand nodes, when the total number of such tilings was, IIRC, on the order of 10^20.
From wikipedia: http://en.wikipedia.org/wiki/Soft_heap
In computer science, the soft heap, designed by Bernard Chazelle in 2000, is a variant on the simple heap data structure. By carefully "corrupting" (increasing) the keys of at most a certain fixed percentage of values in the heap, it is able to achieve amortized constant-time bounds for all five of its operations:
Oh a thanks for the heads-up on this data structure ljlolel.
"In computer science, a kd-tree (short for k-dimensional tree) is a space-partitioning data structure for organizing points in a k-dimensional space. kd-trees are a useful data structure for several applications, such as searches involving a multidimensional search key (e.g. range searches and nearest neighbor searches). kd-trees are a special case of BSP trees." -- http://en.wikipedia.org/wiki/Kd-tree
If you're interested in understanding them, I recommend finding another source for the explanation, like Wikipedia. The ones on stackoverflow can be a little short for an adequate explanation.
Also, there's a limit to how much you can understand by just reading something. Try drawing it out.
And in the end, I'm not sure how much understanding this stuff relates to your need for developers, depending on what you're doing. Most web programming involves getting cogs to fit with other cogs, not advanced algorithms. Obscure data structures are generally obscure for a reason; they're more an intellectual curiosity than anything else ^-^.
I don't think that's necessarily true, the problem is that there are so many of them.
Consider woodworking. A good woodworker has an absolutely enormous array of tools, and all a woodworker does, if you think about it with the mindset of a non-woodworker is remove bits of wood from other bits of wood and put them back together again.
Algorithms are the tools in the toolbox of the programmer, all we do is transform series of bits in to other series of bits. But the number of algorithms is huge compared to the number of tools a woodworker uses, and it is impossible to remember all of them, to pick the one that is 'most appropriate' for the problem at hand.
So we may end up using a sub-optimal solution quite a few times when one of the 'obscure' algorithms would have been more appropriate, just because computers are for the most part fast enough to cover for the inefficiencies.
Toolmaking is one of the nicest forms of programming that I know of anyway.
(pet peeve: browser incompatibilities :( ).
- element trees (etrees), partiocularly lxml implementation. Simple insert and append operations at xpaths (eg /body/html/table/tr[3]/td[7]), iterating over children, accessing element properties as object properties.
- dependency graphs (aka graphs). I have no idea why they're called graphs (I did business, not CS) but anywhere you have dependencies to store and work out (say for a project management app, or a packaging tool) these are the best fit.
They're discussed in detail in volume one of Knuth's "The Art of Computer Programming".
http://www.itl.nist.gov/div897/sqg/dads/
And I feel pretty stupid for not having realized earlier that something like that almost has to exist.
http://www.imada.sdu.dk/~kslarsen/RelBal/
If requests that use a red-black tree tend to come in bursts, then you can probably get speedups by deferring rebalancing for idle periods, even single-threaded. That's pretty darn cool. I would like to see some programming language runtime that watched rb-tree access patterns and decided if this would be a good idea at runtime.