数据结构学习——线段树的C语言实现

关于区间求和求最值问题

如果有一串数组,现在要把其中一段区间的所有数加起来,该怎么办?如果修改了其中的某个值之后再把它们加起来,又该怎么办?

  1. 直接加起来。这样修改值所需时间是O(1),但是求和需要O(n),如果数组一大,就有点慢了。
  2. 做一个前缀数组,数组里第n个值就是原来数组前n个数的和。经过预处理后,求和所需时间是O(1),但修改值后再求和所需时间就变成了O(n),我觉得这和第一种方法一样鸡肋,还更复杂了。
  3. 线段树,也就是我今天学的东西,求和与修改值所需时间之间做了平衡。建树需要O(n),使用时为O(logn)。
  4. ST表,没学。

求最值的思想是一样的,反正线段树挺好。

关于线段树

我的理解

假设数组长度为n,将数组转换成一棵树,树的根节点所储存的树就是第0到第n-1个元素的和,其左右枝分别是第0到第(0+n-1)/2个元素的和与第1+(0+n-1)/2到第n个元素的和,以此类推,最后一定会算成两片叶子(两个元素)的和。
之后求区间和,就一定能分成一或两个树中元素的和,只需要找到这两个元素即可。而修改值也只需要找到相对应的那一条枝干即可。

我需要做的事

  • 建立能将数组转换成一棵树并求和的函数
  • 建立能修改数组以及树中对应的值的函数
  • 建立修改树叶后能重新求和的函数
  • . 建立能求区间和的函数
  • 种植除了能求区间和以外(区间最值)的线段树 orz…再说再说

关于代码

我是看B站up主某月点灯笼学的线段树,码完之后改正美化也是照着来的,所以可以说代码是完全一样的。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//将正常的数组转为一棵树
void build_tree(int array[], int tree[],int root,int op, int ed)
{
	if (op == ed)
		tree[root] = array[op];							//递归尾部,给树叶赋值
	else {
		int mid = (op + ed) / 2;
		int l_son = root * 2 + 1;
		int r_son = root * 2 + 2;
		build_tree(array, tree, l_son, op, mid);		//如果不是叶子,就递归
		build_tree(array, tree, r_son, mid + 1, ed);
		tree[root] = tree[l_son] + tree[r_son];			//树枝等于两片叶子相加
	}
}

//修改树叶之后,整条树枝都要修改
void re_tree(int tree[], int root)
{
	tree[root] = tree[root * 2 + 1] + tree[root * 2 + 2];
	if (root == 0)
		return;
	re_tree(tree, (root - 1) / 2);
}
void change_tree(int array[], int tree[], int root, int op, int ed, int leaf, int num)
{
	if (op == ed)			//找到之后修改叶子
	{
		tree[root] = num;
		array[op] = num;
		re_tree(tree, (root-1) / 2);
	}
	else					//查找树上对应的叶子
	{
		int mid = (op + ed) / 2;
		int l_son = root * 2 + 1;
		int r_son = root * 2 + 2;
		if (op <= leaf && leaf <= mid)
			change_tree(array, tree, l_son, op, mid, leaf, num);
		if (mid < leaf && leaf <= ed)
			change_tree(array, tree, r_son, mid + 1, ed, leaf, num);
	}
}

//区间的树叶求和
int sum_tree(int array[], int tree[], int root, int op, int ed, int l, int r)
{
	if (r < op || ed < l)
		return 0;
	else if (l <= op && ed <= r)
		return tree[root];
	else if (op == ed)
		return tree[root];
	else {
		int mid = (op + ed) / 2;
		int l_son = root * 2 + 1;
		int r_son = root * 2 + 2;
		int l_sum = sum_tree(array, tree, l_son, op, mid, l, r);
		int r_sum = sum_tree(array, tree, r_son, mid + 1, ed, l, r);
		return l_sum + r_sum;
	}
}
int main()
{
	int i, n, m, * arr, * tree;
	scanf("%d", &n);
	arr = (int*)malloc(sizeof(int) * n);	//开拓数组
	for (m = 1; m <= n * 2 - 1; m *= 2);	//树所需要的容量
	tree = (int*)malloc(sizeof(int) * --m);	//种树
	memset(arr, 0, sizeof(int) * n);		//初始化数组
	for (i = 0; i < n; i++)
		scanf("%d", &arr[i]);				//输入数组
	memset(tree, 0, sizeof(int) * m);		//初始化树
	build_tree(arr, tree, 0, 0, n - 1);		//开始构建树
	for (i = 0; i < m; i++)
		printf("%d ", tree[i]); printf("\n");					//测试转换的树
	int leaf, num;												//要修改的叶子
	scanf("%d%d", &leaf, &num);									//输入替换的数
	change_tree(arr, tree, 0, 0, n - 1, leaf-1, num);			//开始改变叶子
	for (i = 0; i < m; i++)
		printf("%d ", tree[i]); printf("\n");					//测试改变的树
	int l, r;													//要求和的区间
	scanf("%d%d", &l, &r);										//输入求和区间
	printf("%d", sum_tree(arr, tree, 0, 0, n - 1, l-1, r-1));	//测试区间求和
	free(tree);		//砍树
	free(arr);
	return 0;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值