Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segment tree enhancements #56

Merged
merged 14 commits into from
Nov 22, 2015
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ using namespace std;
// #endif
const unsigned int N = 14;

// #ifdef hackpackpp
// build the segment tree
//
// data - source array to build segment tree from
// start - the start index of the array (should be 0)
// end - the end index of the array (size - 1)
// tree - the array to hold the segment tree sized appropriately
// st_idx - the current index of the segment tree (should be 0 when first called)
// #endif
int build_segment_tree(int* const data, const unsigned int start, const unsigned int end, int* const tree, const unsigned int st_idx)
{
// #ifdef hackpackpp
// build the segment tree from source data
//
// data - source array to build the segment tree from
// start - the start index of the array (should be 0 when first called)
// end - the end index of the array (size - 1)
// tree - the array to hold the segment tree, sized appropriately
// st_idx - the current index of the segment tree (should be 0 when first called)
// #endif
// #ifdef hackpackpp

// #ifdef hackpackpp
// if start == end, this is a leaf node and should
// have the original value from the array here
Expand Down Expand Up @@ -56,24 +58,30 @@ int build_segment_tree(int* const data, const unsigned int start, const unsigned
return tree[st_idx];
}

// #ifdef hackpackpp
// update a value in the segment tree
//
// to support range sums, this function would have to be modified to recurse
// down the tree to the leaf node before changing values; after the leaf node
// is updated, the new value should be passed back as a return value and each
// parent should update the sum they have by re-adding the values from the
// two children it has
//
// tree - the segment tree with values to be updated
// start - the start index of the _source_ array (should be 0)
// end - the end index of the _source_ array (size - 1)
// changed_idx - the index of the element that changed
// new_val - the new value of the element at changed_idx
// st_idx - the current index of the segment tree (should be 0 when first called)
// #endif

void update_segment_tree(int* const tree, const unsigned int start, const unsigned int end, const unsigned int changed_idx, const int new_val, const unsigned int st_idx)
{
// #ifdef hackpackpp
// update a value in the segment tree
//
// To support range sums, this function would have to be modified to recurse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't strictly relevant to the solving of the problem. Instead of this long comment here, just include segment-tree.cpp in a section called "Reference" and put this comment there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing it. I didn't realize that this was irrelevant when I was going through it. Since the reference code easily does this now, it shouldn't be here any more.

// down the tree to the leaf node before changing values. After the leaf node
// is updated, the new value should be passed back as a return value and each
// parent should update the sum they have by re-adding the values from the
// two children it has.
//
// Alternatively, the old value (or at least some delta) could be an additional
// argument passed in during the original call. Nodes could update to their new
// respective values without waiting for children to report sums back.
//
// tree - the segment tree with values to be updated
// start - the start index of the _source_ array (should be 0)
// end - the end index of the _source_ array (size - 1)
// changed_idx - the index of the element that changed
// new_val - the new value of the element at changed_idx
// st_idx - the current index of the segment tree (should be 0 when first called)
// #endif

// #ifdef hackpackpp
// out of range; should not be counted
// #endif
Expand Down Expand Up @@ -102,19 +110,20 @@ void update_segment_tree(int* const tree, const unsigned int start, const unsign
return;
}

// #ifdef hackpackpp
// find the largest value for a range
//
// tree - the segment tree to query
// start - the start index of the _source_ array
// end - the end index of the _source_ array
// range_start - the start index of the query
// range_end - the end index of the query
// greatest - the greatest value found so far (set to zero when first calling)
// st_idx - the current index in the segtree (set to zero when first calling)
// #endif
int query_segment_tree(const int* const tree, const unsigned int start, const unsigned int end, const unsigned int range_start, const unsigned int range_end, int greatest, const unsigned int st_idx)
{
// #ifdef hackpackpp
// find the largest value for a range
//
// tree - the segment tree to query
// start - the start index of the _source_ array
// end - the end index of the _source_ array
// range_start - the start index of the query
// range_end - the end index of the query
// greatest - the greatest value found so far (set to zero when first calling)
// st_idx - the current index in the segtree (set to zero when first calling)
// #endif

// #ifdef hackpackpp
// out of range; do not continue
// #endif
Expand Down
108 changes: 83 additions & 25 deletions structures/segment-tree/segment-tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@
#include <cmath>
using namespace std;

// dummy query returns; modify as necessary
#define ST_QUERY_DUMMY_MAX 99999999
#define ST_QUERY_DUMMY_MIN -99999999
#define ST_QUERY_DUMMY_SUM 0

// choose information to store
enum SegmentTreeType { ST_MIN, ST_MAX, ST_SUM };
const SegmentTreeType type = ST_MIN;

// number of entries in array
unsigned int N;
unsigned int N = 10;

int segment_tree_helper(const int a, const int b)
{
if(type == ST_MIN)
return (a < b ? a : b);
else if(type == ST_MAX)
return (a > b ? a : b);
else if(type == ST_SUM)
return (a + b);
else
return -1;
}

int build_segment_tree(int* const data, const unsigned int start, const unsigned int end, int* const tree, const unsigned int st_idx)
{
Expand All @@ -17,56 +38,81 @@ int build_segment_tree(int* const data, const unsigned int start, const unsigned
const int left = build_segment_tree(data, start, mid, tree, (2 * st_idx) + 1);
const int right = build_segment_tree(data, mid + 1, end, tree, (2 * st_idx) + 2);

if(left > right) tree[st_idx] = left;
else tree[st_idx] = right;
tree[st_idx] = segment_tree_helper(left, right);

return tree[st_idx];
}

void update_segment_tree(int* const tree, const unsigned int start, const unsigned int end, const unsigned int changed_idx, const int new_val, const unsigned int st_idx)
void update_segment_tree(int* const tree, const unsigned int start, const unsigned int end, const unsigned int changed_idx, const int old_val, const int new_val, const unsigned int st_idx)
{
if(changed_idx < start || changed_idx > end)
return;

if(new_val > tree[st_idx] || start == end)
tree[st_idx] = new_val;
if(type == ST_MIN)
{
if(new_val < tree[st_idx] || start == end)
tree[st_idx] = new_val;
}
else if(type == ST_MAX)
{
if(new_val > tree[st_idx] || start == end)
tree[st_idx] = new_val;
}
else if(type == ST_SUM)
{
int delta = new_val - old_val;
tree[st_idx] += delta;
}


if(start == end)
return;

const unsigned int mid = start + ((end - start) / 2);
update_segment_tree(tree, start, mid, changed_idx, new_val, (2 * st_idx) + 1);
update_segment_tree(tree, mid + 1, end, changed_idx, new_val, (2 * st_idx) + 2);
update_segment_tree(tree, start, mid, changed_idx, old_val, new_val, (2 * st_idx) + 1);
update_segment_tree(tree, mid + 1, end, changed_idx, old_val, new_val, (2 * st_idx) + 2);

return;
}

int query_segment_tree(const int* const tree, const unsigned int start, const unsigned int end, const unsigned int range_start, const unsigned int range_end, int greatest, const unsigned int st_idx)
int query_segment_tree(const int* const tree, const unsigned int start, const unsigned int end, const unsigned int range_start, const unsigned int range_end, int val, const unsigned int st_idx)
{
if(start > range_end || end < range_start)
return -2;
{
if(type == ST_MIN)
return ST_QUERY_DUMMY_MAX;
else if(type == ST_MAX)
return ST_QUERY_DUMMY_MIN;
else if(type == ST_SUM)
return ST_QUERY_DUMMY_SUM;
}

if(start == end)
return tree[st_idx];

if(start >= range_start && end <= range_end)
if(type == ST_MIN && (start >= range_start && end <= range_end))
{
if(tree[st_idx] < val)
return tree[st_idx];
}
else if(type == ST_MAX && (start >= range_start && end <= range_end))
{
if(tree[st_idx] > greatest)
if(tree[st_idx] > val)
return tree[st_idx];
}
else if(type == ST_SUM && (start >= range_start && end <= range_end))
{
return tree[st_idx];
}
else
{
const unsigned int mid = start + ((end - start) / 2);
const int left = query_segment_tree(tree, start, mid, range_start, range_end, greatest, (2 * st_idx) + 1);
const int right = query_segment_tree(tree, mid + 1, end, range_start, range_end, greatest, (2 * st_idx) + 2);

if(left > greatest)
greatest = left;
if(right > greatest)
greatest = right;
const int left = query_segment_tree(tree, start, mid, range_start, range_end, val, (2 * st_idx) + 1);
const int right = query_segment_tree(tree, mid + 1, end, range_start, range_end, val, (2 * st_idx) + 2);
val = segment_tree_helper(left, right);
}

return greatest;
return val;
}

int main()
Expand All @@ -80,11 +126,23 @@ int main()
build_segment_tree(src_array, 0, N - 1, segtree, 0);

// update value in segtree
src_array[42] = 43;
update_segment_tree(segtree, 0, N - 1, 42, 43, 0);

// query segtree between indices 0 and 12, inclusive
int result = query_segment_tree(segtree, 0, N - 1, 0, 12, -1, 0);
int old = src_array[4];
src_array[4] = 43;
update_segment_tree(segtree, 0, N - 1, 4, old, 43, 0);
old = src_array[2];
src_array[2] = 21;
update_segment_tree(segtree, 0, N - 1, 2, old, 21, 0);
old = src_array[3];
src_array[3] = 2;
update_segment_tree(segtree, 0, N - 1, 3, old, 2, 0);
old = src_array[9];
src_array[9] = 1;
update_segment_tree(segtree, 0, N - 1, 9, old, 1, 0);

// query segtree between indices 0 and 9, inclusive
int result = query_segment_tree(segtree, 0, N - 1, 0, 9, ST_QUERY_DUMMY_MAX, 0);

cout << result << endl;

delete[] segtree;

Expand Down
3 changes: 2 additions & 1 deletion structures/segment-tree/segment-tree.tex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ \section{Segment Tree}
The prefix sum calculation only takes $O(n)$ time to perform, and any query thereafter takes only $O(1)$ time.
But any updates to the source dataset will require $O(n)$ time to update the prefix sum calculations.
Frequent changes will quickly show the pitfall of this approach.
This precalculation approach will also not work for finding minimum or maximum values in an arbitrary range.
A segment tree is a data structure for storing information about intervals that can be constructed in $O(n)$ time.
Whenever the source data changes, update times stay low at $O(\log n)$ time.

Expand All @@ -33,7 +34,7 @@ \section{Segment Tree}

A segment tree is represented as an array of a size that is dependent on the dataset it is sourced from.
For a given array of size $n$, a segment tree constructed from it will use up to $2^{\lceil \log_2 (n)\rceil + 1} - 1$ space.
To properly represent tree, the root is located at index 0; its left and right children are located at indices 1 and 2 respectively.
To properly represent the tree, the root is located at index 0; its left and right children are located at indices 1 and 2 respectively.
To properly recurse through the tree, indices of left and right children can be calculated with $2n + 1$ and $2n + 2$ for left and right children respectively.

Construction of a segment tree begins at the root.
Expand Down
Loading