**Summary**

 Type BST (*) Heap
 Insert average log(n) 1
 Insert worst log(n) log(n) or n (***)
 Find any worst log(n) n
 Find max worst 1 (**) 1
 Create worst n log(n) n
 Delete worst log(n) log(n)

All average times on this table are the same as their worst times except for Insert.

- `*`: everywhere in this answer, BST == Balanced BST, since unbalanced sucks asymptotically
- `**`: using a trivial modification explained in this answer
- `***`: `log(n)` for pointer tree heap, `n` for dynamic array heap

**Advantages of binary heap over a BST**

- average time insertion into a binary heap is `O(1)`, for BST is `O(log(n))`. **This** is the killer feature of heaps.

 There are also other heaps which reach `O(1)` amortized (stronger) like the [Fibonacci Heap](https://en.wikipedia.org/wiki/Fibonacci_heap), and even worst case, like the [Brodal queue](https://en.wikipedia.org/wiki/Brodal_queue), although they may not be practical because of non-asymptotic performance: https://stackoverflow.com/questions/30782636/are-fibonacci-heaps-or-brodal-queues-used-in-practice-anywhere

- binary heaps can be efficiently implemented on top of either [dynamic arrays](https://en.wikipedia.org/wiki/Dynamic_array) or pointer-based trees, BST only pointer-based trees. So for the heap we can choose the more space efficient array implementation, if we can afford occasional resize latencies.

- binary heap creation [is `O(n)` worst case](https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap), `O(n log(n))` for BST.

**Advantage of BST over binary heap**

- search for arbitrary elements is `O(log(n))`. **This** is the killer feature of BSTs.

 For heap, it is `O(n)` in general, except for the largest element which is `O(1)`. 

**"False" advantage of heap over BST**

- heap is `O(1)` to find max, BST `O(log(n))`.

 This is a common misconception, because it is trivial to modify a BST to keep track of the largest element, and update it whenever that element could be changed: on insertion of a larger one swap, on removal find the second largest. https://stackoverflow.com/questions/7878622/can-we-use-binary-search-tree-to-simulate-heap-operation (mentioned [by Yeo](https://stackoverflow.com/a/27074221/895245)).

 Actually, this is a *limitation* of heaps compared to BSTs: the *only* efficient search is that for the largest element.

**Average binary heap insert is `O(1)`**

Sources:

- Paper: http://i.stanford.edu/pub/cstr/reports/cs/tr/74/460/CS-TR-74-460.pdf
- WSU slides: http://www.eecs.wsu.edu/~holder/courses/CptS223/spr09/slides/heaps.pdf

Intuitive argument:

- bottom tree levels have exponentially more elements than top levels, so new elements are almost certain to go at the bottom
- heap insertion [starts from the bottom](https://en.wikipedia.org/wiki/Binary_heap#Insert), BST must start from the top

In a binary heap, increasing the value at a given index is also `O(1)` for the same reason. But if you want to do that, it is likely that you will want to keep an extra index up-to-date on heap operations https://stackoverflow.com/questions/17009056/how-to-implement-ologn-decrease-key-operation-for-min-heap-based-priority-queu e.g. for Dijkstra. Possible at no extra time cost.

**GCC C++ standard library insert benchmark on real hardware**

I benchmarked the C++ `std::set` ([Red-black tree BST](https://stackoverflow.com/questions/2558153/what-is-the-underlying-data-structure-of-a-stl-set-in-c/51944661#51944661)) and `std::priority_queue` ([dynamic array heap](https://stackoverflow.com/questions/11266360/when-should-i-use-make-heap-vs-priority-queue/51945521#51945521)) insert with [this code](https://github.com/cirosantilli/cpp-cheat/blob/9d0f77792fc8e55b20b6ee32018761ef3c5a3f2f/cpp/interactive/bst_vs_heap.cpp) and [this plot script](https://github.com/cirosantilli/cpp-cheat/blob/9d0f77792fc8e55b20b6ee32018761ef3c5a3f2f/cpp/interactive/bst_vs_heap.gnuplot) to see if I was right about the insert times, and this is what I got:

[![enter image description here][1]][1]

So clearly:

- heap insert time is basically constant.

 We can clearly see dynamic array resize points. Since we are averaging every 10k inserts [to be able to see anything at all above system noise](https://stackoverflow.com/questions/51952471/why-do-i-get-a-constant-instead-of-logarithmic-curve-for-an-insert-time-benchmar/51953081#51953081), those peaks are in fact about 10k times larger than shown!

 The zoomed graph excludes essentially only the array resize points, and shows that almost all inserts fall under 25 nanoseconds.

- BST is logarithmic. All inserts are much slower than the average heap insert.

Ubuntu 18.04, GCC 7.3, Intel i7-7820HQ CPU, DDR4 2400 MHz RAM, Lenovo Thinkpad P51.

**GCC C++ standard library insert benchmark on gem5**

[gem5](http://gem5.org/) is a full system simulator, and therefore provides an infinitely accurate clock with with `m5 dumpstats`. So I tried to use it to estimate timings for individual inserts.

[![enter image description here][2]][2]

Interpretation:

- heap is still constant, but now we see in more detail that there are a few lines, and each higher line is more sparse.

 This must correspond to memory access latencies are done for higher and higher inserts.

- TODO I can't really interpret the BST fully one as it does not look so logarithmic and somewhat more constant.

 With this greater detail however we can see can also see a few distinct lines, but I'm not sure what they represent: I would expect the bottom line to be thinner, since we insert top bottom?

Benchmarked with this [Buildroot setup](https://github.com/cirosantilli/linux-kernel-module-cheat/tree/7ccc1d3a8fa02967422cd2d25fe08e23d060db95#bst-vs-heap) on an aarch64 [HPI CPU](https://github.com/cirosantilli/linux-kernel-module-cheat/tree/7ccc1d3a8fa02967422cd2d25fe08e23d060db95#gem5-run-benchmark).

**BST cannot be efficiently implemented on an array**

Heap operations only need to bubble up or down a single tree branch, so `O(log(n))` worst case swaps, `O(1)` average.

Keeping a BST balanced requires tree rotations, which can change the top element for another one, and would require moving the entire array around (`O(n)`).

**Heaps can be efficiently implemented on an array**

Parent and children indexes can be computed from the current index [as shown here]( http://web.archive.org/web/20180819074303/https://www.geeksforgeeks.org/array-representation-of-binary-heap/).

There are no balancing operations like BST.

Delete min is the most worrying operation as it has to be top down. But it can always be done by "percolating down" a single branch of the heap [as explained here](https://en.wikipedia.org/w/index.php?title=Binary_heap&oldid=849465817#Extract). This leads to an O(log(n)) worst case, since the heap is always well balanced.

If you are inserting a single node for every one you remove, then you lose the advantage of the asymptotic O(1) average insert that heaps provide as the delete would dominate, and you might as well use a BST. Dijkstra however updates nodes several times for each removal, so we are fine. 

**Dynamic array heaps vs pointer tree heaps**

Heaps can be efficiently implemented on top of pointer heaps: https://stackoverflow.com/questions/19720438/is-it-possible-to-make-efficient-pointer-based-binary-heap-implementations

The dynamic array implementation is more space efficient. Suppose that each heap element contains just a pointer to a `struct`:

- the tree implementation must store three pointers for each element: parent, left child and right child. So the memory usage is always `4n` (3 tree pointers + 1 `struct` pointer).

 Tree BSTs would also need further balancing information, e.g. black-red-ness.

- the dynamic array implementation can be of size `2n` just after a doubling. So on average it is going to be `1.5n`.

On the other hand, the tree heap has better worst case insert, because copying the backing dynamic array to double its size takes `O(n)` worst case, while the tree heap just does new small allocations for each node.

Still, the backing array doubling is `O(1)` amortized, so it comes down to a maximum latency consideration. [Mentioned here](https://stackoverflow.com/a/41338070/895245).

**Philosophy**

- BSTs maintain a global property between a parent and all descendants (left smaller, right bigger).

 The top node of a BST is the middle element, which requires global knowledge to maintain (knowing how many smaller and larger elements are there).

 This global property is more expensive to maintain (log n insert), but gives more powerful searches (log n search).

- Heaps maintain a local property between parent and direct children (parent > children).

 The top note of a heap is the big element, which only requires local knowledge to maintain (knowing your parent).

**Doubly-linked list**

A doubly linked list can be seen as subset of the heap where first item has greatest priority, so let's compare them here as well:

- insertion:
 - position:
 - doubly linked list: the inserted item must be either the first or last, as we only have pointers to those elements.
 - binary heap: the inserted item can end up in any position. Less restrictive than linked list.
 - time:
 - doubly linked list: `O(1)` worst case since we have pointers to the items, and the update is really simple
 - binary heap: `O(1)` average, thus worse than linked list. Tradeoff for having more general insertion position.
- search: `O(n)` for both

An use case for this is when the key of the heap is the current timestamp: in that case, new entries will always go to the beginning of the list. So we can even forget the exact timestamp altogether, and just keep the position in the list as the priority.

This can be used to implement an [LRU cache](https://stackoverflow.com/a/34206517/895245). Just like [for heap applications like Dijkstra](https://stackoverflow.com/questions/14252582/how-can-i-use-binary-heap-in-the-dijkstra-algorithm), you will want to keep an additional hashmap from the key to the corresponding node of the list, to find which node to update quickly.

**BST vs Hash map**

Some nice graphs at: https://stackoverflow.com/questions/18414579/what-data-structure-is-inside-stdmap-in-c/51945119#51945119

**See also**

Similar question on SO: https://stackoverflow.com/questions/6147242/heap-vs-binary-search-tree-bst

 [1]: https://i.sstatic.net/7eGsx.png
 [2]: https://i.sstatic.net/reK3u.png