Finger Searching in Data Structure

A finger search on a data structure is defined as an extension of any search operation that structure supports, where a reference (finger) to an element in the data structure is given along with the query. While the search time for an element is most frequently denoted as a function of the number of elements in a data structure, finger search times are treated as a function of the distance between the element and the finger.

In a set of m elements, the distance d(a, b) between two elements a and b is their difference in rank. If elements a and b are the i-th and j-th largest elements in the structure, then the difference in rank is |i - j|. If a normal search in some structure would normally consume O(f(m)) time, then a finger search for element a with finger element b should ideally consume O(f(d)) time. Remark that since d ≤ m, it follows that in the worst case, finger search is only as bad as normal search. However, in practice these degenerate finger searches perform more work than normal searches. For instance, if f(n) is log(n), and finger search performs twice as many comparisons as normal search in the worst case, it follows that finger search is slower when d > √n. Therefore, finger search should only be implemented when one can reasonably expect the target to actually be close to the finger.


Some popular data structures support finger search without additional changes to the actual structure. In structures where searching for an element a is performed by narrowing down an interval over which a can be found, finger search from element b is typically performed by reversing the search process from b until the search interval is large enough to contain element a, at which point search proceeds as normal.

Sorted linked lists

In a linked list, one normally searches in a linear manner for an element by traversing from one end to the other. If the linked list is sorted, and we have a reference to some node containing element b, then we may find element a in O(d) time by starting our search from element b.

Sorted arrays

In a sorted array B, one normally searches for an element a in B with a binary search. Finger search is performed by implementing a one-sided search from B[j] = b. While binary search halves the search space after each comparison, one-sided search doubles the search space after each comparison. Specifically, on the kth iteration of one-sided search (assuming a>b), the interval under consideration is B[j, j+2k-1]. Expansion halts as soon as B[j + 2k-1] ≥ a, at which point this interval is binary searched for element a. If one-sided search consumes k iterations to find an interval that contains element a, then it follows that d > 2k-2. Binary searching this range will also consume another k iteration. Therefore, finger search for a from b consumes O(k) = O(log d) time


Skip lists

In a skip list, one can finger search for element a from a node containing the element b by simply continuing the search from this point. Note that if a < b, then search proceeds at backward direction, and if a > b, then search proceeds at forward direction. The backwards case is symmetric to normal search in a skip list, but the forward case is actually more complicated. Normally, search in a skip list is expected to be fast because the sentinel at the start of the list is considered as the tallest node. However, our finger could be a node of height 1. Because of this, we may rarely climb while trying to search; something which never occurs normally. However, even with this complication, we can achieve O(log d) expected search time.


A treap is defined as a randomized binary search tree (BST). Searching in a treap is the similar as searching for an element in any other BST. Treaps however have the property that the expected path length between two elements of distanced is denoted as O(log d). Thus, to finger search from the node containing element b for element a, one can climb the tree from b element until an ancestor of element a is found, at which point normal BST search proceeds as usual manner. While calculating if a node is the ancestor of another is non-trivial, one may augment the tree to support queries of this form to provide expected O(log d) finger search time.

Ropes and trees

Implementations of the rope (data structure) typically use a cord position iterator to visit the string. The iterator can be considered as a finger that points at some specific character of the string. Like most balanced trees, ropes require O(log(m)) time to retrieve data in one leaf of the tree when given only the root of the tree. Reading every leaf of the tree would seem to require O(m*log(m)) time. However, by storing a little extra information, the iterator can be implemented to read "the next" leaf in only O(1) time, and every leaf of a tree in only O(m) time.