title: Why is insertion into my tree faster on sorted input than random input?
body: <p>Now I've always heard binary search trees are faster to build from randomly selected data than ordered data, simply because ordered data requires explicit rebalancing to keep the tree height at a minimum.</p>
  
  <p>Recently I implemented an immutable <a href="http://en.wikipedia.org/wiki/Treap" rel="noreferrer">treap</a>, a special kind of binary search tree which uses randomization to keep itself relatively balanced. In contrast to what I expected, I found I can consistently build a treap about 2x faster and generally better balanced from ordered data than unordered data -- and I have no idea why.</p>
  
  <p>Here's my treap implementation:</p>
  
  <ul>
  <li><a href="http://pastebin.com/VAfSJRwZ" rel="noreferrer">http://pastebin.com/VAfSJRwZ</a></li>
  </ul>
  
  <p>And here's a test program:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Diagnostics;
  
  namespace ConsoleApplication1
  {
  
      class Program
      {
          static Random rnd = new Random();
          const int ITERATION_COUNT = 20;
  
          static void Main(string[] args)
          {
              List&lt;double&gt; rndTimes = new List&lt;double&gt;();
              List&lt;double&gt; orderedTimes = new List&lt;double&gt;();
  
              rndTimes.Add(TimeIt(50, RandomInsert));
              rndTimes.Add(TimeIt(100, RandomInsert));
              rndTimes.Add(TimeIt(200, RandomInsert));
              rndTimes.Add(TimeIt(400, RandomInsert));
              rndTimes.Add(TimeIt(800, RandomInsert));
              rndTimes.Add(TimeIt(1000, RandomInsert));
              rndTimes.Add(TimeIt(2000, RandomInsert));
              rndTimes.Add(TimeIt(4000, RandomInsert));
              rndTimes.Add(TimeIt(8000, RandomInsert));
              rndTimes.Add(TimeIt(16000, RandomInsert));
              rndTimes.Add(TimeIt(32000, RandomInsert));
              rndTimes.Add(TimeIt(64000, RandomInsert));
              rndTimes.Add(TimeIt(128000, RandomInsert));
              string rndTimesAsString = string.Join("\n", rndTimes.Select(x =&gt; x.ToString()).ToArray());
  
              orderedTimes.Add(TimeIt(50, OrderedInsert));
              orderedTimes.Add(TimeIt(100, OrderedInsert));
              orderedTimes.Add(TimeIt(200, OrderedInsert));
              orderedTimes.Add(TimeIt(400, OrderedInsert));
              orderedTimes.Add(TimeIt(800, OrderedInsert));
              orderedTimes.Add(TimeIt(1000, OrderedInsert));
              orderedTimes.Add(TimeIt(2000, OrderedInsert));
              orderedTimes.Add(TimeIt(4000, OrderedInsert));
              orderedTimes.Add(TimeIt(8000, OrderedInsert));
              orderedTimes.Add(TimeIt(16000, OrderedInsert));
              orderedTimes.Add(TimeIt(32000, OrderedInsert));
              orderedTimes.Add(TimeIt(64000, OrderedInsert));
              orderedTimes.Add(TimeIt(128000, OrderedInsert));
              string orderedTimesAsString = string.Join("\n", orderedTimes.Select(x =&gt; x.ToString()).ToArray());
  
              Console.WriteLine("Done");
          }
  
          static double TimeIt(int insertCount, Action&lt;int&gt; f)
          {
              Console.WriteLine("TimeIt({0}, {1})", insertCount, f.Method.Name);
  
              List&lt;double&gt; times = new List&lt;double&gt;();
              for (int i = 0; i &lt; ITERATION_COUNT; i++)
              {
                  Stopwatch sw = Stopwatch.StartNew();
                  f(insertCount);
                  sw.Stop();
                  times.Add(sw.Elapsed.TotalMilliseconds);
              }
  
              return times.Average();
          }
  
          static void RandomInsert(int insertCount)
          {
              Treap&lt;double&gt; tree = new Treap&lt;double&gt;((x, y) =&gt; x.CompareTo(y));
              for (int i = 0; i &lt; insertCount; i++)
              {
                  tree = tree.Insert(rnd.NextDouble());
              }
          }
  
          static void OrderedInsert(int insertCount)
          {
              Treap&lt;double&gt; tree = new Treap&lt;double&gt;((x, y) =&gt; x.CompareTo(y));
              for(int i = 0; i &lt; insertCount; i++)
              {
                  tree = tree.Insert(i + rnd.NextDouble());
              }
          }
      }
  }
  </code></pre>
  
  <p>And here's a chart comparing random and ordered insertion times in milliseconds:</p>
  
  <pre><code>Insertions         Random          Ordered         RandomTime / OrderedTime
  50                 1.031665        0.261585        3.94
  100                0.544345        1.377155        0.4
  200                1.268320        0.734570        1.73
  400                2.765555        1.639150        1.69
  800                6.089700        3.558350        1.71
  1000               7.855150        4.704190        1.67
  2000               17.852000       12.554065       1.42
  4000               40.157340       22.474445       1.79
  8000               88.375430       48.364265       1.83
  16000              197.524000      109.082200      1.81
  32000              459.277050      238.154405      1.93
  64000              1055.508875     512.020310      2.06
  128000             2481.694230     1107.980425     2.24
  </code></pre>
  
  <p>I don't see anything in the code which makes ordered input asymptotically faster than unordered input, so I'm at a loss to explain the difference.</p>
  
  <p><strong>Why is it so much faster to build a treap from ordered input than random input?</strong></p>