Various implementations of Balanced Binary Search Tree (BST) guaranteed membership queries in O(log⁡n)O(\log n) time, where nn is the number of elements in some set SS. The working set property is a stronger requirement than O(log⁡n)O(\log n) time complexity. Suppose that an item x∈Sx \in S is accessed tt queries before, then we want to perform find(x)\text{find} (x) in O(log⁡t)O(\log t) time.

The idea is to maintain a sequence of Balanced BST's, denoted as T0,T1,T2,...T_0, T_1, T_2, .... Denote the size of TiT_i for some i≥0i \ge 0 as nin_i. Then the ii-th tree maintains the latest nin_i accessed items. The sizes of nin_i's satisfy

n0=1n1=2n2=4n3=16...ni=22i−1ni+1=ni2=22i...nk=22k−1 \begin{aligned} & n_0 = 1 \\ & n_1 = 2 \\ & n_2 = 4 \\ & n_3 = 16 \\ & ... \\ & n_i = 2^{2^{i - 1} } \\ & n_{i + 1} = n_i^2 = 2^{2^i } \\ & ... \\ & n_k = 2^{2^{k - 1} } \end{aligned}

The largest tree has an index that equals to the smallest kk with 2k−1≥log⁡n2^{k - 1} \ge \log n (assuming that n≥1n \ge 1). It implies that k=O(log⁡log⁡n)k = O (\log \log n). The complexities for searching an element on these trees are

c0=1+log⁡1c1=1+log⁡2c2=1+log⁡4c3=1+log⁡16...ck=1+log⁡22k−1 \begin{aligned} & c_0 = 1 + \log 1 \\ & c_1 = 1 + \log 2 \\ & c_2 = 1 + \log 4 \\ & c_3 = 1 + \log 16 \\ & ... \\ & c_k = 1 + \log 2^{2^{k - 1}}\\ \end{aligned}

When a query find(x)\text{find} (x) is invoked, we first search it on T0T_0. If we find it successfully, we stop. Otherwise we search it on T1T_1 and so on. If xx is accessed tt queries before (and is not accessed in the latest t−1t - 1 queries if t≥1t \ge 1), then it is contained in the tree TixT_{i_x}, where

ix≐ the smallest non-negative integer, s.t. 2ix−1≥t i_x \doteq \text{ the smallest non-negative integer, } s.t. \ 2^{i_x - 1} \ge t

As a sanity check, note that when t=0t = 0 (xx is the latest accessed item), we have ix=0i_x = 0 as 2−1≥02^{-1} \ge 0. By definition of definition of ixi_x, xx is not contained in any TiT_i with i<ixi < i_x. The time to search xx is

(1+log⁡1)+(1+log⁡2)+(1+log⁡4)+(1+log⁡16)+...+(1+log⁡22ix−1)=ix+1+∑i=0ix−12i=ix+1+2ix−1=O(log⁡log⁡t)+O(log⁡t)=O(log⁡t) \begin{aligned} &(1 + \log 1) + (1 + \log 2) + (1 + \log 4) + (1 + \log 16) + ... + (1 + \log 2^{2^{i_x - 1} }) \\ &=i_x + 1 + \sum_{i = 0}^{i_x - 1} 2^{i} \\ &=i_x + 1 + 2^{i_x} - 1 \\ &=O(\log \log t) + O(\log t) \\ &=O(\log t) \end{aligned}

Finally, we need to update the trees T0T_0, T1T_1, ... TixT_{i_x} to reflect the last access of xx. In particular, we 1) insert xx into T0T_0, T1T_1, ... Tix−1T_{i_x - 1}; 2) for each i<ixi < i_x, we delete the least recently accessed item; 3) update the access time of xx in TixT_{i_x}.

Step 1) takes O(log⁡t)O(\log t) time. To implement steps 2) and 3) in O(log⁡t)O(\log t) time, we create a min-heap HiH_i for each tree TiT_i that keeps the the access time of items in TiT_i. Both fetching least recently accessed item and updating access time can be performed in O(log⁡ni)O(\log n_i) time. Therefore, steps 2) and 3) take O(log⁡t)O(\log t) time in total.