Why We Drop Lower Terms

Recall the implementation of remove in DynamicArray and SortedArray:

public boolean remove(T value) {
  int i = indexOf(value);     // linear or binary search?
  if (i == -1) {
    return false;             // not here
  }
  for (int j = i; j < size - 1; j++) {
    arr[j] = arr[j + 1];      // shift the rest one slot left
  }
  size--;
  arr[size] = null;           // clear the vacated slot
  return true;
}

This method does two things. First it finds the element, and then it shifts everything after it one slot to the left. Let’s analyze each part.

The cost of indexOf is where the two implementations differ. The DynamicArray version of indexOf uses linear search, which is . The SortedArray version uses binary search, which is .

The shifting is the same in both. The loop copies every element after position i one slot left, so it runs size - 1 - i times. In the worst case we remove the first element, i is , and the loop runs times. So the shifting is in both implementations.

Putting the two parts together:

  • DynamicArray.remove does for the linear search plus for the shifting, so .
  • SortedArray.remove does for the binary search plus for the shifting, so .

Suppressing the coefficient turns into , so DynamicArray.remove is , and we justified that in the previous section.

Dropping the lower term turns into , so SortedArray.remove is also . Why are we allowed to drop the lower term? Let’s look at some numbers. Assume a processor that does a million instructions per second, and see how long and take. The table also shows for comparison:

size

Look at the row for a million. The column costs a second and the column costs less than a second. Adding the two together still costs about a second. The term does not add anything you could measure. At a billion, the column costs about 17 minutes and the column still costs less than a second. The larger gets, the less the term matters. That is why we drop it. Work of steps is asymptotically, because the term is negligible next to the term as grows.

The same argument works for any pair of terms where one grows faster. If the work is , look at the million row again: the term costs 12 days and the and terms together cost about a second. Twelve days plus a second is twelve days. So we drop the lower terms and say the work is .

This is what the second step is for. On large inputs the dominant term is almost all of the cost, so the terms we drop were not contributing much anyway.