LintCode 202. Segment Tree Query (线段树经典题!)

本文介绍了一种使用线段树查询区间最大值的方法,并提供了两种递归实现方式。同时对比了前缀和数组、差分数组、线段树及树状数组在处理区间问题时的应用场景。
  1. Segment Tree Query
    中文English
    For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end).

Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree.

Example 1:
For array [1, 4, 2, 3], the corresponding Segment Tree is:

Input:"[0,3,max=4][0,1,max=4][2,3,max=3][0,0,max=1][1,1,max=4][2,2,max=2][3,3,max=3]",1,2
Output:4
Explanation:
For array [1, 4, 2, 3], the corresponding Segment Tree is :

	                  [0, 3, max=4]
	                 /             \
	          [0,1,max=4]        [2,3,max=3]
	          /         \        /         \
	   [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3]
The maximum value of [1,2] interval is 4

Input:query(root, 1, 1), Output:4

Input:query(root, 1, 2), Output:4

Input:query(root, 2, 3), Output:5

Input:query(root, 0, 2), Output:4

Example 2:

Input:"[0,3,max=4][0,1,max=4][2,3,max=3][0,0,max=1][1,1,max=4][2,2,max=2][3,3,max=3]",2,3
Output:3
Explanation:
For array [1, 4, 2, 3], the corresponding Segment Tree is :

	                  [0, 3, max=4]
	                 /             \
	          [0,1,max=4]        [2,3,max=3]
	          /         \        /         \
	   [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3]
The maximum value of [2,3] interval is 3

Notice
It is much easier to understand this problem if you finished Segment Tree Build first.

解法1:递归。
注意:

  1. mid必须以root->start和root->end为准,而不是start和end为准。因为线段树就是根据root->start和root->end来划分的。
  2. start和end都是与mid比较,而不是与root->start和root->end比较。
/**
 * Definition of SegmentTreeNode:
 * class SegmentTreeNode {
 * public:
 *     int start, end, max;
 *     SegmentTreeNode *left, *right;
 *     SegmentTreeNode(int start, int end, int max) {
 *         this->start = start;
 *         this->end = end;
 *         this->max = max;
 *         this->left = this->right = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param root: The root of segment tree.
     * @param start: start value.
     * @param end: end value.
     * @return: The maximum number in the interval [start, end]
     */
    int query(SegmentTreeNode * root, int start, int end) {
        if (!root) return 0;
        
        if (start <= root->start && end >= root->end) return root->max;
        int mid = root->start + (root->end - root->start) / 2;
        int result = 0;
        if (start <= mid) {
            result = max(result, query(root->left, start, min(mid, end)));
        }
        if (mid + 1 <= end) {
            result = max(result, query(root->right, max(mid + 1, start), end));            
        }
        
        return result;
    }
};

解法2:和解法1类似。参考九章。
区别就是这里查询的start和end一直都没变。为什么可以不变呢?因为最终递归时,比如说选左区间,如果start在左区间,end超出了左区间,那么最终左区间又会继续递归。假如start<新左区间的mid,则继续递归query start和新左区间的左区间,以及新左区间的右区间,而后者已经在[start,end]范围内。

class Solution {
public:
    /**
     * @param root: The root of segment tree.
     * @param start: start value.
     * @param end: end value.
     * @return: The maximum number in the interval [start, end]
     */
    int query(SegmentTreeNode * root, int start, int end) {
        if (!root) return 0;
        
        if (start <= root->start && end >= root->end) return root->max;
        int mid = root->start + (root->end - root->start) / 2;
        int result = 0;
        if (start <= mid) {
            result = max(result, query(root->left, start, end));
        }
        if (mid + 1 <= end) {
            result = max(result, query(root->right, start, end));            
        }
        
        return result;
    }
};

另外,根据链接:https://blog.youkuaiyun.com/weixin_55516868/article/details/128717574
前缀和数组、差分数组、线段树、树状数组均用于处理区间问题,但有不同应用场景

前缀和数组:用于处理 连续多次取区间和 操作的情况,取区间和期间不能对原数组进行区间增减数操作

差分数组:用于处理 多次区间增减数操作,最后读取一次区间和(即修改期间读取不频繁) 操作的情况
线段树:用于处理 多次区间增减数操作,期间多次读取区间和(即修改期间读取频繁) 操作的情况
树状数组:用于处理 多次区间增减数操作,期间多次读取区间和(即修改期间读取频繁) 操作的情况,同线段树的情况、但树状数组更加简单且不支持区间更新,线段树支持区间更新但编码复杂

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值