线段树(待补充)

主要的作用是求解数组的 区间和修改数组元素
以往主要是利用两种算法:1.朴素算法 2.前缀和

这里把求区间和叫做query 修改数组元素update


1.朴素算法

在执行update的时候时间复杂度O(1)
执行query时候时间见复杂度O(n)

2.前缀和

在执行update的时候时间复杂度O(n)
执行query时候时间见复杂度O(1)

上面的两种算法在都有各自的有点,但是在同时处理两个问题的时候,就显得有些吃力了,O(n)的复杂度有很大的限制。解决方法就是利用线段树这种数据结构,它可以把两种操作的时间复杂度降到O(logn)


线段树

先补充一个前备的知识点:假设有一树状数组tree[MAX_LEN],对于某个节点node
左 儿 子 : l e f t n o d e [ i n d e x ] , i n d e x = 2 ∗ n o d e + 1 左儿子:leftnode[index],index=2*node + 1 :leftnode[index],index=2node+1 右 儿 子 : r i g h t n o d e [ i n d e x ] , i n d e x = 2 ∗ n o d e + 2 右儿子:rightnode[index],index=2*node + 2 :rightnode[index],index=2node+2
前提有这样一个数组int arr[10] = {1, 3, 5, 7, 9, 11};

根据已有数组建树

这个过程类似于堆排序那样,所有的叶节点保存的是arr[]数组的每一个元素,中间节点保存的是以这个节点为根节点的树的值;

const int MAX_LEN = 1000;
int len = 6;
int tree[MAX_LEN];
int arr[10] = {1, 3, 5, 7, 9, 11};
void build_tree(int *arr, int *tree, int node, int star, int end)
{
	if (star == end)
	{
		tree[node] = arr[star];
		// 递归的出口,这时的 node 相当于这个树的叶节点
	}
	else
	{
		int mid = (star + end) / 2; 
		int left_node = (2 * node) + 1;
		int right_node = (2 * node) + 2;

		build_tree(arr, tree, left_node, star, mid);
		build_tree(arr, tree, right_node, mid + 1, end);
		tree[node] = tree[left_node] + tree[right_node];
	}
}

在这里插入图片描述
就会按这种格式建立一个树;

update_num

修改arr[]数组中指定位置元素的值,对给定index value 使arr[index]=value

void update_num(int *arr, int *tree, int node, int star, int end, int index, int value)
{
	if (star == end)
	{
		arr[index] = value;
		tree[node] = value;
	}
	else
	{
		int mid = (star + end) / 2;
		int left_node = (2 * node) + 1;
		int right_node = (2 * node) + 2;

		if (index >= star && index <= mid)
		{
			update_num(arr, tree, left_node, star, mid, index, value);
		}
		else
		{
			update_num(arr, tree, right_node, mid + 1, end, index, value);
		}

		tree[node] = tree[left_node] + tree[right_node];
		// 回溯修改各个节点的值;
	}
}

这个过程和上面的建树过程很相似,建树是不断的把,这个arr[]左右分开递归调用建立起来的;修改不断的把左右区间的范围和index比较直到找到这个位置就修改,然后用同样的方法回溯修改各个父节点的值;

query_sum

对于给定的左右边界求arr[]在这个区间的和

int query_sum(int *arr, int *tree, int node, int star, int end, int L, int R)
{
	cout << "star = " << star << " end = " << end << endl;
	// 在递归过程中如果这个区间不在这个要求的区间内就可以直接返回0
	if (L > end || R < star)
	{
		return 0;
	}// 找到单个节点就返回
	else if (star == end)
	{
		return tree[node];
	}// 找到区间值,返回
	else if (star >= L && end <= R)
	{
		return tree[node];
	}
	else
	{
		int mid = (star + end) / 2;
		int left_node = (2 * node) + 1;
		int right_node = (2 * node) + 2;

		int left_sum  = query_sum(arr, tree, left_node, star, mid, L, R);
		int right_sum = query_sum(arr, tree, right_node, mid + 1, end, L, R);
		return left_sum + right_sum;
	}
}
区间修改(update_section)

不会!!!!

区间最大值

有了上面的基础求区间最大值就变的简单了,在求区间和的时候,各个节点上保存的是,以这个结点作为根节点的树的和,改为保存这个区间的最大值就好了; 单点修改时,记得要把最后回溯的过程也改为求左右儿子的最大值

const int MAX_LEN = 1000;
int len = 6;
int tree[MAX_LEN];
int arr[10] = {1, 3, 5, 7, 9, 11};
void build_tree(int *arr, int *tree, int node, int star, int end)
{
	if (star == end)
	{
		tree[node] = arr[star];
		// 递归的出口,这时的 node 相当于这个树的叶节点
	}
	else
	{
		int mid = (star + end) / 2; 
		int left_node = (2 * node) + 1;
		int right_node = (2 * node) + 2;

		build_tree(arr, tree, left_node, star, mid);
		build_tree(arr, tree, right_node, mid + 1, end);
		tree[node] = max(tree[left_node], tree[right_node]);//比较左右儿子保存最大值
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值