lintcode-线段树

本文对线段树做总结,主要给出基本概念以及相应的实际问题。
主要参考了这篇博客[一步一步理解线段树]

基本概念

线段树

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。

例子

下面我们从一个经典的例子来了解线段树,问题描述如下:从数组arr[0…n-1]中查找某个数组某个区间内的最小值,其中数组大小固定,但是数组中的元素的值可以随时更新。

一般解法

对这个问题一个简单的解法是:遍历数组区间找到最小值,时间复杂度是O(n),额外的空间复杂度O(1)。当数据量特别大,而查询操作很频繁的时候,耗时可能会不满足需求。

另一种解法:

另一种解法:使用一个二维数组来保存提前计算好的区间[i,j]内的最小值,那么预处理时间为O(n^2),查询耗时O(1), 但是需要额外的O(n^2)空间,当数据量很大时,这个空间消耗是庞大的,而且当改变了数组中的某一个值时,更新二维数组中的最小值也很麻烦。

我们可以用线段树来解决这个问题:

复杂度如下:

  • 预处理耗时O(n),因为对于含有N元素的线段树而言,大概要生成2n个节点。
  • 查询、更新操作O(logn),这是优于O(N)的。
  • 需要额外的空间O(n)

操作

创建线段树

题目:[创建线段树-I]

/**
 * Definition of SegmentTreeNode:
 * class SegmentTreeNode {
 * public:
 *     int start, end;
 *     SegmentTreeNode *left, *right;
 *     SegmentTreeNode(int start, int end) {
 *         this->start = start, this->end = end;
 *         this->left = this->right = NULL;
 *     }
 * }
 */
class Solution {
public:
    /**
     *@param start, end: Denote an segment / interval
     *@return: The root of Segment Tree
     */
    SegmentTreeNode * build(int start, int end) {
        // write your code here
        if( start > end ) return NULL;
        SegmentTreeNode* root = new SegmentTreeNode( start, end );
        if(start == end) return root;

        int mid = (start+end)/2;
        root->left = build( start, mid );
        root->right = build( mid + 1, end );

        return root;

    }
};

题目:[创建线段树-II]

/**
 * 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 A: a list of integer
     *@return: The root of Segment Tree
     */
    SegmentTreeNode * build(vector<int>& A) {
        // write your code here
        int sz = A.size();
        if(!sz) return NULL;

        return build(A,  0, sz-1 );

    }
private:
    SegmentTreeNode* build(vector<int>& A, int start, int end ){
        if(start > end) return NULL;
        SegmentTreeNode* root = new SegmentTreeNode(start, end, 0);
        if( start == end ){
            root->max = A[start];
        }
        else{
            int mid = (start + end)/2;
            root->left = build(A, start, mid);
            root->right = build(A, mid+1, end);
            root->max = std::max( root->left->max, root->right->max );
        }
        return root;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值