{"id":37084357,"url":"https://github.com/jyuv/segment-tree","last_synced_at":"2026-01-14T10:19:16.356Z","repository":{"id":65420052,"uuid":"552106391","full_name":"jyuv/segment-tree","owner":"jyuv","description":"A generic python3 implementation of segment tree and dual segment tree data structures. Supporting generic inputs and non-commutative functions.","archived":false,"fork":false,"pushed_at":"2022-10-17T14:11:30.000Z","size":2172,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-23T02:38:07.666Z","etag":null,"topics":["algorithms","data-structures","graphs","python","range-query","segment-tree","tree","tree-structure"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jyuv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-15T20:40:40.000Z","updated_at":"2022-10-17T15:13:22.000Z","dependencies_parsed_at":"2023-01-23T10:55:14.879Z","dependency_job_id":null,"html_url":"https://github.com/jyuv/segment-tree","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jyuv/segment-tree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jyuv%2Fsegment-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jyuv%2Fsegment-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jyuv%2Fsegment-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jyuv%2Fsegment-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jyuv","download_url":"https://codeload.github.com/jyuv/segment-tree/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jyuv%2Fsegment-tree/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416899,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:18:03.274Z","status":"ssl_error","status_checked_at":"2026-01-14T10:16:11.865Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["algorithms","data-structures","graphs","python","range-query","segment-tree","tree","tree-structure"],"created_at":"2026-01-14T10:19:15.867Z","updated_at":"2026-01-14T10:19:16.351Z","avatar_url":"https://github.com/jyuv.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Generic Segment Tree\n\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n![LicenseLink](https://img.shields.io/badge/license-MIT-blue.svg)\n\n\nA generic python3 implementation of segment tree and dual segment tree\ndata structures. The implementation is not only supporting generic inputs but\nalso supports non-commutative functions and non-commutative composition\nof functions.\n\nA segment tree is a data structure useful for storing items in a way\nwhich makes it easy to update individual elements and query for the cumulative\nvalue of applying a certain function to items' segments/intervals.\n\nA dual segment tree is a data structure useful for applying a series of \nfunctions on all items in a segment/interval individually, while also allowing\nto query for specific items.\n\n\n## How to use?\n\n#### Installation\n\nInstall using pip from the command line:\n`pip install seg-tree`\n\n#### Segment Tree\nAs seen below you can use arrays of any type and any binary-associative functions\n(even non-commutative like chars concatenation)\n\n```python\nfrom segment_tree import SegmentTree\n\n# ints with multiplication function\narr = [1, 2, 3, 4, 5, 6, 7, 8]\nst = SegmentTree(items=arr, func=lambda x, y: x * y)\nst.update(item_id=3, new_val=9) # items' values after: [1, 2, 3, 9, 5, 6, 7, 8]\nst.query(left=0, right=2) # 6 (= 1 * 2 * 3)\nst.query(left=2, right=5) # 810 (= 3 * 9 * 5 * 6)\n\n# chars with concatenation function\narr = ['a', 'b', 'c', 'd', 'e', 'f', 'g']\nst = SegmentTree(items=arr, func=lambda x, y: x + y)\nst.update(item_id=3, new_val='r')  # items' values after: ['a', 'b', 'c', 'r', 'e', 'f', 'g']\nst.query(left=0, right=2)  # \"abc\"\nst.query(left=2, right=5)  # \"cref\"\n```\n\n#### Dual Segment Tree\nAs seen below you can compose any associative series of unary functions\nand use arrays of any type\n\n```python\nfrom segment_tree import DualSegmentTree\n\n# Example with ints array\narr = [1, 2, 3, 4, 5, 6, 7, 8]\nst = DualSegmentTree(arr)\nst.update(left=1, right=3, func=lambda x: x + 5) # items' values after: [1, 7, 8, 9, 5, 6, 7, 8]\nst.update(left=2, right=4, func=lambda x: -x) # items' values after: [1, 7, -8, -9, -5, 6, 7, 8]\nst.query(item_id=0) # 1\nst.query(item_id=3) # -9\n\n# Example with chars array\narr = ['a', 'b', 'c', 'd', 'e', 'f', 'g']\ndef concat_r(x): return x + 'r'\nst = DualSegmentTree(items=arr)\nst.update(left=3, right=5, func=concat_r) # items' values after: ['a', 'b', 'c', 'dr', 'er', 'fr', 'g']\nst.query(item_id=1) # \"b\"\nst.query(item_id=4) # \"er\"\n```\n\n\n## API and Time Complexity\nDenote `n` as the total number of elements in the array.\n\n#### Segment Tree\n\n| Function | Description | Time Complexity\n| ------ |---------|----------\n| `SegmentTree(items, func)` | Builds a segment tree from `items` with `func` as cumulative function  | O(n)        \n| `update(item_id, new_val)` | Updates an the content of item in `item_id` to `new_val`| O(log n)\n| `query(left, right)` | Returns the cumulative value of applying `func` to`[left, right]`| O(log n)\n\n\n#### Dual Segment Tree\n\n| Function | Description | Time Complexity\n| ------ |---------|----------\n| `DualSegmentTree(items)` | Builds a dual segment tree from `items` | O(n)        \n| `update(left, right, func)` | Apply `func` to each of the items in `[left, right]` individually| O(log n) Amortized\n| `query(item_id)` | Get the value of item with id=`item_id`| O(log n)\n\n\n## Further Explanation On The Algorithms\nThe segment tree creates an array that simulates a complete binary tree\nin which the leaves are the items of the input array. This tree is composed of\nroughly `2 * n - 1` nodes (in the case where n is not a power of 2, additional dummy\nleaves are added to maintain the complete binary tree structure).\n\nThe tree is taking advantage of the non-leaves nodes to store there values\nwhich will help it to perform actions on segments of the items without going\nall the way down to all of the items.\n\n### (Regular) Segment Tree\n\n**build** - Fill the leaves with the values from the input array and fill the\nrest of the tree applying the provided function on `(left_child, right_child)`.\n\n**update** - The tree updates the relevant leaf and its ancestors all the way\nup to the root.\n\n**query** - Here the idea is to start at the root of the tree and go down\nlooking for the `split_node`. The `split_node` is the lowest node in the tree\nthat all the requested query segment is contained in its dominating segment of leaves.\nAn example of a `split_node` is shown below, where the blue node is the split\nnode for the segment marked in black. \n\u003cbr\u003e\n\u003cp align=center\u003e\n\u003cimg src=\"https://github.com/jyuv/segment_tree/raw/main/assets/split_node.png?raw=true\"\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\nFrom the `split_node` the algorithm takes 2 tours - one to each of the children of `split_node`.\nTaking the left tour it goes to the left child of `split_node`. Looking at the\nleft child of `split_node` there are 3 options:\n\n1. The dominating segment of `split_node.left` is contained within the requested \nquery segment. In this, case the tour is ending with `split_node.left.val`.\n\u003cbr\u003e\n\u003cp align=center\u003e\n\u003cimg src=\"https://github.com/jyuv/segment_tree/raw/main/assets/case_contained.png?raw=true\"\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\n2. The dominating segment of `split_node.left.left` intersects with the requested\nquery segment. This means that the whole dominating segment of `split_node.left.right`\nis contained within the query segment, so all of its leaves descendants are relevant.\nTherefore `split_node.left.right.val` is memorized aside and the tour continues to \n`split_node.left.left` which will result in some arbitrary value `left_val`. The algorihm\nthan returns `func(left_val, split_node.left.right.val)` as the left tour's result.\n\u003cbr\u003e\n\u003cp align=center\u003e\n\u003cimg src=\"https://github.com/jyuv/segment_tree/raw/main/assets/case_left_intersects.png?raw=true\"\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\n3. The dominating segment of `split_node.left.left` **doesn't** intersects with the requested\nquery segment. This means the tour can be continued to `split_node.left.right`\nand return the result of it as the result of the whole left tour.\n\u003cbr\u003e\n\u003cp align=center\u003e\n\u003cimg src=\"https://github.com/jyuv/segment_tree/raw/main/assets/case_left_not_intersects.png?raw=true\"\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\nThe right tour is done similarly and it returns `func(left_tour_result, right_tour_result)` \n\nThis whole method maintains narrow routes, not spreading to many allies, which helps\nkeep the time complexity low.\n\n### Dual Segment Tree\n\n**build** - Fill the leaves with the values from the input array and fill the\nrest of the tree with the identity function.\n\n**query** - Start from the relevant leaf and go up to the root composing the\nfunctions on the way one over the others.\n\n**update** - Works similarly to the SegmentTree's query. There is an additional\ntweak here to allow non-commutative series of functions composition (cases where\n`f(g(x)) != g(f(x))`). To achieve this it does as follows: during the update,\non the way down from the root the non-identity it functions it meets on the way are pushed\ndownwards to their children. An illustration of this in a simple case is shown below.\n\n\u003cbr\u003e\n\u003cp align=center\u003e\n\u003cimg src=\"https://github.com/jyuv/segment_tree/raw/main/assets/giffy.gif?raw=true\"\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjyuv%2Fsegment-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjyuv%2Fsegment-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjyuv%2Fsegment-tree/lists"}