{"id":19446637,"url":"https://github.com/jishanshaikh4/size-balanced-tree","last_synced_at":"2025-04-25T01:32:11.770Z","repository":{"id":48964626,"uuid":"382482881","full_name":"jishanshaikh4/size-balanced-tree","owner":"jishanshaikh4","description":"Complete introduction to size balanced trees (O(1) amortized complexity)","archived":false,"fork":false,"pushed_at":"2021-07-02T23:31:35.000Z","size":61,"stargazers_count":18,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-03T14:43:36.984Z","etag":null,"topics":["competitive-coding","competitive-programming","trees"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jishanshaikh4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-02T23:08:37.000Z","updated_at":"2024-05-20T11:11:02.000Z","dependencies_parsed_at":"2022-09-24T01:01:19.963Z","dependency_job_id":null,"html_url":"https://github.com/jishanshaikh4/size-balanced-tree","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jishanshaikh4%2Fsize-balanced-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jishanshaikh4%2Fsize-balanced-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jishanshaikh4%2Fsize-balanced-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jishanshaikh4%2Fsize-balanced-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jishanshaikh4","download_url":"https://codeload.github.com/jishanshaikh4/size-balanced-tree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250738167,"owners_count":21479141,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["competitive-coding","competitive-programming","trees"],"created_at":"2024-11-10T16:14:37.430Z","updated_at":"2025-04-25T01:32:11.523Z","avatar_url":"https://github.com/jishanshaikh4.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Size Balanced Tree\nA size balanced tree (SBT) is a self-balancing binary search tree (SBBST) rebalanced by examining the sizes of each node's subtrees (instead of fields such as randomized weights in treaps or colours in red–black tress). It supports the standard binary search tree operations such as insertion, deletion, and searching in O(log n) time.\n\n## Properties\nThe size balanced tree examines each node's size (i.e. the number of nodes below it in the subtree) to determine when rotations should be performed. Each node T in the tree satisfies the following two properties: \n- size(T.left) \u003e= size(T.right.left), size(T.right.right)\n- size(T.right) \u003e= size(T.left.left), size(T.left.right)\n\nIn other words, each child node of T is not smaller in size than the child nodes of its sibling. Clearly, we should consider the sizes of nonexistent children and siblings to be 0.\n\nConsider the following example where T is the node in question, L, R are its child nodes, and A, B, C, D are subtrees which also satisfy the above SBT properties on their own. \n```\n       T\n     /    \\\n    L       R\n  /   \\    /  \\\n A     B  C    D\n```\nThen, the node T must satisfy: \n- size(L) \u003e= size\\(C\\), size(D)\n- size\\(R\\) \u003e= size(A), size(B)\n\n## Rotations\nThe rotations of SBTs are analogous to those in other self-balancing binary search trees. \n```\n  -------------    Right Rotation     ------------\n  |    Q      |   ---------------\u003e    |     P    |\n  |   / \\     |                       |    / \\   |\n  -- P   C    |                       |   A   Q --\n    / \\    \u003c---     Left Rotation     ---\u003e   / \\  \n   A   B          \u003c---------------          B   C \n```\n### Left Rotation\n```\ndef left-rotate(t):\n    k ← t.right\n    t.right ← k.left\n    k.left ← t\n    k.size ← t.size\n    t.size ← t.left.size + t.right.size + 1\n    t ← k\n```\n### Right Rotation\n```\ndef right-rotate(t):\n    k ← t.left\n    t.left ← k.right\n    k.right ← t\n    k.size ← t.size\n    t.size ← t.left.size + t.right.size + 1\n    t ← k\n```\n\n## Maintenance\nAfter insertions and deletions, the new sizes of subtrees may violate the two properties above. Thus, we define a procedure maintain(T) to rebalance the SBT rooted at the node T. This should be called with the precondition that T's children are already SBTs themselves. Since property 1 and 2 are symmetrical, we can only discuss property 1.\n\nThere are 4 cases to consider when rebalancing. \n- **Case 1:** size(T.left) \u003c size(T.right.left)\n Perhaps after inserting a value to T.right, the scenario below (figure 1) may occur, leading to size(L) \u003c size\\(C\\). To fix this, we first perform a right-rotate on T.right (figure 2) and then a left-rotate on T (figure 3).\n```\n    Fig. 1:                Fig. 2:                   Fig. 3:    \n  insert(R,v)          right-rotate(R)            left-rotate(T)\n\n       T                      T                         C       \n      / \\                    / \\                       / \\      \n     /   \\                  /   \\                     /   \\     \n    L     R                L     C                   T     R    \n   / \\   / \\              / \\   / \\                 / \\   / \\   \n  A   B C   D            A   B E   R               L   E F   D  \n       / \\                        / \\             / \\           \n      E   F                      F   D           A   B          \n```\nAfter these operations, the properties of the entire tree in figure 3 becomes unpredictable. Luckily, the subtrees A, B, D, E, F, L are still SBTs. Thus, we can recursively call maintain on subtrees R and T to take care of them.\nNow that all of the subtrees are SBTs, we still have to make sure that the root node C satisfies the SBT properties. So, we call maintain one last time on root node C.\n\n- **Case 2:** size(T.left) \u003c size(T.right.right)\nPerhaps after inserting a value to T.right, the scenario below (figure 4) may occur, leading to size(L) \u003c size(D). This is similar to case 1, except that instead of going below C, E and F instead goes below D. We can omit them from the diagram. Fixing this, we will perform a left-rotate on the root node T, obtaining the structure in figure 5.\n```\n    Fig. 4:                Fig. 5:   \n  insert(R,v)           left-rotate(T)\n       T                       R           \n      / \\                     / \\          \n     /   \\                   /   \\         \n    L     R                 T     D        \n   / \\   / \\               / \\             \n  A   B C   D             L   C            \n                         / \\               \n                        A   B              \n```\nAfter this, the tree rooted at R is still not yet a SBT because size\\(C\\) \u003c size(A) or size\\(C\\) \u003c size(B) may be true. So, we continue to call maintain on T.\nNow that we have satisfied the precondition of making R's subtrees SBTs, we may call maintain on R itself.\n- **Case 3:** size(T.right) \u003c size(T.left.right)\nSymmetrical to case 1.\n- **Case 4:** size(T.right) \u003c size(T.left.left)\nSymmetrical to case 2.\n\nWith this casework being taken care of, it becomes straightforward to actually implement maintain. \n```\ndef maintain(t):\n    if t.left.size \u003c t.right.left.size:         // case 1\n        right-rotate(t.right)\n        left-rotate(t)\n        maintain(t.left)\n        maintain(t.right)\n        maintain(t)\n    else if t.left.size \u003c t.right.right.size:   // case 2\n        left-rotate(t)\n        maintain(t.left)\n        maintain(t)\n    else if t.right.size \u003c t.left.right.size:   // case 1'\n        left-rotate(t.left)\n        right-rotate(t)\n        maintain(t.left)\n        maintain(t.right)\n        maintain(t)\n    else if t.right.size \u003c t.left.left.size:    // case 2'\n        right-rotate(t)\n        maintain(t.right)\n        maintain(t)\n```\nThis pseudocode is slightly slow and redundant. Since we know that the two SBT properties will usually be satisfied, the following is an optimization. Simply add an extra boolean flag to the maintain parameters, indicating whether cases 1/2 or their symmetrical cases are being examined. If the flag is TRUE, then we examine cases 1 and 2, otherwise we examine cases 3 and 4. Doing so will eliminate many unnecessary comparisons. \n```\ndef maintain(t, flag):\n    if flag:\n        if t.left.size \u003c t.right.left.size:        // case 1\n            right-rotate(t.right)\n            left-rotate(t)\n        else if t.left.size \u003c t.right.right.size:  // case 2\n            left-rotate(t)\n        else:\n            done\n    else:\n        if t.right.size \u003c t.left.right.size:       // case 1'\n            left-rotate(t.left)\n            right-rotate(t)\n        else if t.right.size \u003c t.left.left.size:   // case 2'\n            right-rotate(t)\n        else:\n            done\n    maintain(t.left, FALSE)   // maintain the left subtree\n    maintain(t.right, TRUE)   // maintain the right subtree\n    maintain(t, TRUE)         // maintain the whole tree\n    maintain(t, FALSE)        // maintain the whole tree\n```\nIt is easy to visualize that maintain(t.left, TRUE) and maintain(t.right, FALSE) are unnecessary. Furthermore, the running time of maintain is O(1) amortized (which means that you do not have to worry about it not terminating). \n\n## Fundamental Operations\n### Searching\nSearching in SBTs is exactly the same as searching in other binary search trees. The following iterative implementation will return a pointer to the node in the SBT rooted at t which has key k.\n```\ndef search(t, k):\n   x ← t\n   while x is not NIL:\n       if k \u003c x.key then x ← x.left\n       else if x.key \u003c k then x ← x.right\n       else return x\n   return NIL   // key not found!\n```\n### Get Max/Min\nThe size of the SBT is already stored. These operations can thus be handled trivially by the select operation implemented in the section below. \n### Iteration\nIterating a SBT is exactly the same as iterating a normal binary search tree (by repeatedly finding nodes' predecessors/successors). \n### Insertion\nInserting into a SBT is very simple. The only difference from normal binary search trees is that it has an extra call to maintain at the end. The following recursive version will insert the node x into the SBT rooted at t. \n```\ndef insert(t, x):\n    if x is NIL:\n        t ← x\n    else\n        t.size ← t.size + 1\n        if x.key \u003c t.key:\n            insert(t.left, x)\n        else\n            insert(t.right, x)\n    maintain(t, x.key ≥ t.key)\n```\n### Deletion\nDeletion is exactly the same as in normal binary search trees. It is not even necessary to call maintain afterwards. The proof for this is as follows: A SBT will have all of its properties before deletion. Even though we cannot guarantee that the SBT will retain its balanced properties after the insertion, we know for sure that its height (and thus, its running time) will not increase. Given this, it is clear that calling maintain after deleting is extraneous. \n\n## Order Statistics\nSince SBTs already conveniently store the size field to maintain balance, nothing else is needed to transform it into a fully-fledged order statistics tree. \n### Select\nThe following function returns a pointer to the ith smallest element in the SBT rooted at t, where i is zero-indexed. To make this one-indexed, simply change \"r ← t.left.size\" to \"r ← t.left.size + 1\" and \"i - (r + 1)\" to \"i - r\". \n```\ndef select(t, i):\n    r ← t.left.size\n    if i = r:\n        return t\n    else if i \u003c r:\n        return select(t.left, i)\n    else \n        return select(t.right, i - (r + 1))\n```\n### Rank\nDetermining the rank of an element in a SBT is exactly the same as doing so for a regular binary search tree. \n\n## References\n\n1. Chen, Qifeng. \"Size Balanced Tree\", Guandong, China, 29 December 2006.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjishanshaikh4%2Fsize-balanced-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjishanshaikh4%2Fsize-balanced-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjishanshaikh4%2Fsize-balanced-tree/lists"}