[算法与数据结构]-线段树

前言

对于给出一个一维数组,求出数组中指定区间内的最大值这个区间最大值问题,最直接的方法就是暴力遍历数组的指定区间,时间复杂度为O(n)。线段树就是用于解决这类的问题的一种数据结构。它也是一种二叉树,不过节点存的数据是区间的起始坐标以及该区间的最大值,也就是说每一个节点存的是一个区间以及区间内最大值
以 [5,2,6,1] 为例:

线段树示例

代码实现

结构定义

typedef struct SegmentTreeNode{
	int start;
	int end;
	int max;
	struct SegmentTreeNode *left,*right;
}SegmentTreeNode,*SegmentTree;

接口实现

常用宏定义:

#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TRUE 1
#define FALSE 0
#define SUCCESS 1
#define INT_MINVALUE -2147483648
//根据数组,起始下标以及截止下标,递归创建一棵线段树(注意这里的线段树传的是引用形参)
Status createSegmentTree(SegmentTree &T,int start,int end,int *num){
	//参数有误,起始下标大于截止下标或数组为空
	if(start > end || num == NULL) return ERROR;
	if(T == NULL){
		T = (SegmentTree)malloc(sizeof(SegmentTreeNode));
		if(T == NULL) return OVERFLOW;
	}
	T->max = INT_MINVALUE;
	T->left = T->right = NULL;
	//递归边界,当当前区间只剩下一个数
	if(start == end){
		T->start = start;
		T->end = end;
		T->max = num[start];
		T->left = T->right = NULL;
		return TRUE;
	}
	T->start = start;
	T->end = end;
	int mid = (end + start) >> 1;
	//递归构建左右两边区间
	createSegmentTree(T->left,start,mid,num);
	createSegmentTree(T->right,mid + 1,end,num);
	//左右子树(区间)构建好后,左右子树(区间)两个最大值中较大的就是整个区间的最大值
	if(T->left != NULL) T->max = T->left->max > T->max  ? T->left->max : T->max;
	if(T->right != NULL) T->max = T->right->max > T->max ? T->right->max : T->max;
	return TRUE;
}
//查找线段树中指定区间的最大值
int findSegmentMax(SegmentTree T,int start,int end){
	//参数有误
	if(start > end) return INT_MINVALUE;
	//参数有误,要查询的区间范围已经超过当前节点的区间范围
	if(start < T->start || end > T->end) return INT_MINVALUE;
	//区间就是当前节点的区间
	if(start == T->start && end == T->end) return T->max;
	int mid = (T->start + T->end) >> 1;
	//要查找的区间包含了当前节点的区间的中间值,就要以中间值为界拆分为两半分别查找
	//如当前节点区间为[0,7],要查找的区间为[2,5],就要查找在当前节点的左子树查找[2,3]的最大值,
	//再在右子树查找[4,5]的最大值,两次查找到最大值的较大值就是[2,5]的最大值
	if(end >= mid && start <= mid){
		int rightMax = findSegmentMax(T->right,mid + 1,end);
		int leftMax = findSegmentMax(T->left,start,mid);
		return rightMax > leftMax ? rightMax : leftMax;
	}else if(start > mid){
		//要查找的区间在当前区间的中间值的右边,直接查找当前节点的右子树即可
		return findSegmentMax(T->right,start,end);
	}else if(end < mid){
		//要查找的区间在当前区间的中间值的左边,直接查找当前节点的左子树即可
		return findSegmentMax(T->left,start,end);
	}
}

时间复杂度分析

在建树操作中,数组每一个元素都会被处理到,所以复杂度是O(n)
分析查找操作之前要先知道:由于每一次都是对区间左右平分到左右子树中,所以最后树的高度会是logn。所以就算查找时一直找到了叶子节点,复杂度也会是O(logn)。如果是对一个数组进行多次求区间最大值的操作,暴力做的话每一次都需要O(n)的复杂度,使用线段树的话,只需要先花O(n)的复杂度建树,以后每次查找只需要O(logn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值