线段树基础

来源:互联网 发布:ubuntu 在线音乐 编辑:程序博客网 时间:2024/05/21 18:35

1.线段树的构造

线段树是一棵二叉树,他的每个节点包含了两个额外的属性startend用于表示该节点所代表的区间。start和end都是整数,并按照如下的方式赋值:

  • 根节点的 start 和 end 由 build 方法所给出。
  • 对于节点 A 的左儿子,有 start=A.left, end=(A.left + A.right) / 2
  • 对于节点 A 的右儿子,有 start=(A.left + A.right) / 2 + 1, end=A.right
  • 如果 start 等于 end, 那么该节点是叶子节点,不再有左右儿子。

实现一个 build 方法,接受 start 和 end 作为参数, 然后构造一个代表区间 [start, end] 的线段树,返回这棵线段树的根。

比如给定start=1, end=6,对应的线段树为:

               [1,  6]             /        \      [1,  3]           [4,  6]      /     \           /     \   [1, 2]  [3,3]     [4, 5]   [6,6]   /    \           /     \[1,1]   [2,2]     [4,4]   [5,5]
代码:

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(end<start)return nullptr;        SegmentTreeNode *t=new SegmentTreeNode(start,end);        if(start!=end)        {t->left=build(start,(start+end)/2);        t->right=build((start+end)/2+1,end);}        return t;    }};

2.线段树的查询

对于一个有n个数的整数数组,在对应的线段树中, 根节点所代表的区间为0-n-1, 每个节点有一个额外的属性max,值为该节点所代表的数组区间start到end内的最大值。

为SegmentTree设计一个 query 的方法,接受3个参数rootstartend,线段树root所代表的数组中子区间[start, end]内的最大值。

对于数组 [1, 4, 2, 3], 对应的线段树为:

                  [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]

query(root, 1, 1), return 4

query(root, 1, 2), return 4

query(root, 2, 3), return 3

query(root, 0, 2), return 4

代码:

class Solution {public:    /**     *@param root, start, end: The root of segment tree and      *                         an segment / interval     *@return: The maximum number in the interval [start, end]     */    int query(SegmentTreeNode *root, int start, int end) {        // write your code here        if(root->left==nullptr)return root->max;        if(root->start>=start&&root->end<=end)return root->max;        int mid=(root->start+root->end)/2;                if(start>mid)return  query(root->right,start,end);        else if(end<=mid)query(root->left,start,end);        else return max(query(root->left,start,mid),query(root->right,mid+1,end));    }};
3.线段树的修改

For a Maximum Segment Tree, which each node has an extra value maxto store the maximum value in this node's interval.

Implement a modify function with three parameter rootindex and value to change the node's value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value.

Example

For segment tree:

                      [1, 4, max=3]                    /                \        [1, 2, max=2]                [3, 4, max=3]       /              \             /             \[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3]

if call modify(root, 2, 4), we can get:

                      [1, 4, max=4]                    /                \        [1, 2, max=4]                [3, 4, max=3]       /              \             /             \[1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3]

or call modify(root, 4, 0), we can get:

                      [1, 4, max=2]                    /                \        [1, 2, max=2]                [3, 4, max=0]       /              \             /             \[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0]
代码:

class Solution {public:    /**     *@param root, index, value: The root of segment tree and      *@ change the node's value with [index, index] to the new given value     *@return: void     */    void modify(SegmentTreeNode *root, int index, int value) {        // write your code here        if(root->start==index&&root->end==index)        {            root->max=value;                    }        else if(root->left==nullptr)return;        else        {            modify(root->left,index,value);            modify(root->right,index,value);            root->max=max(root->left->max,root->right->max);                   }    }};
4.区间最小数

给定一个整数数组(下标由 0 到 n-1,其中 n 表示数组的规模),以及一个查询列表。每一个查询列表有两个整数 [start, end]。 对于每个查询,计算出数组中从下标 start 到 end 之间的数的最小值,并返回在结果列表中。

样例

对于数组 [1,2,7,8,5], 查询 [(1,2),(0,4),(2,4)],返回 [2,1,5]

代码:
/** * Definition of Interval: * classs Interval { *     int start, end; *     Interval(int start, int end) { *         this->start = start; *         this->end = end; *     } */ class tree { public:      int start, end, min;      tree *left, *right;      tree(int start, int end) {          this->start = start;          this->end = end;          this->min = min;         this->left = this->right = NULL;      }  };class Solution {    public:    /**     *@param A, queries: Given an integer array and an query list     *@return: The result list     */      tree* build(vector<int> &A,int start,int end)    {        if(start==end)        {            tree*t=new tree(start,end);            t->min=A[start];            return t;        }        int mid=(start+end)/2;        tree*t=new tree(start,end);        t->left=build(A,start,mid);        t->right=build(A,mid+1,end);        t->min=min(t->left->min,t->right->min);                        return t;    }    int query(tree*t,int start,int end)    {        int mid=(t->start+t->end)/2;        if(start<=t->start&&end>=t->end)return t->min;        else if(end<=mid)return query(t->left,start,end);        else if(start>mid)return query(t->right,start,end);        else return min(query(t->left,start,mid),query(t->right,mid+1,end));    }    vector<int> intervalMinNumber(vector<int> &A, vector<Interval> &queries) {        // write your code here        int len=A.size();        vector<int>ret;        tree*t=build(A,0,len-1);        for(int i=0;i<queries.size();i++)        {            tree*t1=t;            Interval val=queries[i];            int start=val.start;            int end=val.end;            ret.push_back(query(t1,start,end));                    }        return ret;            }};

5.区间求和

在类的构造函数中给一个整数数组, 实现两个方法 query(start, end) 和 modify(index, value):

  • 对于 query(startend), 返回数组中下标 start 到 end 的 
  • 对于 modify(indexvalue), 修改数组中下标为 index 上的数为 value.
样例

给定数组 A = [1,2,7,8,5].

  • query(0, 2), 返回 10.
  • modify(0, 4), 将 A[0] 修改为 4.
  • query(0, 1), 返回 6.
  • modify(2, 1), 将 A[2] 修改为 1.
  • query(2, 4), 返回 14.
class mytree{    public:    int start,end;    long long sum;    mytree* left,*right;    mytree(int start,int end)    {        this->start=start;        this->end=end;        this->left=this->right=nullptr;        this->sum=0;    }};class Solution {public:    /* you may need to use some attributes here */        /**     * @param A: An integer vector     */     mytree*t;     mytree* build(vector<int>&nums,int start,int end)     {         if(start==end)         {             mytree *t1=new mytree(start,end);             t1->sum=nums[start];             return t1;         }         else          {             mytree *t1=new mytree(start,end);             int mid=(start+end)/2;             t1->left=build(nums,start,mid);             t1->right=build(nums,mid+1,end);             t1->sum=t1->left->sum+t1->right->sum;             return t1;         }     }    Solution(vector<int> A) {        // write your code here        if(A.size()==0)t=nullptr;        else{        int start=0;        int end=A.size()-1;        t=build(A,start,end);        }            }        /**     * @param start, end: Indices     * @return: The sum from start to end     */    long long query(int start, int end) {        // write your code here        if(t==nullptr)return 0;        return q(t,start,end);    }    long long q(mytree*&t,int start,int end)    {        if(start==t->start&&end==t->end)return t->sum;         else         {             int mid=(t->start+t->end)/2;             if(start>mid)return q(t->right,start,end);             else if(end<=mid)return q(t->left,start,end);             else return q(t->right,mid+1,end)+q(t->left,start,mid);         }    }        /**     * @param index, value: modify A[index] to value.     */    void modify(int index, int value) {        // write your code here        if(t)        m(t,index,value);            }    void m(mytree *&t,int index,int value)    {        if(index==t->start&&index==t->end)        {            t->sum=value;        }        else        {            int mid=(t->start+t->end)/2;            if(index<=mid)            {                m(t->left,index,value);                t->sum=t->left->sum+t->right->sum;            }            else            {                m(t->right,index,value);                t->sum=t->left->sum+t->right->sum;            }        }    }};

6.统计前面比自己小的数的个数

给定一个整数数组 (下标由 0 到 n-1,其中 n 表示数组的规模,数值范围由 0 到 10000),以及一个 查询列表。对于每一个查询,将会给你一个整数,请你返回该数组中小于给定整数的元素的数量。

样例

对于数组 [1,2,7,8,5] ,查询 [1,8,5],返回 [0,4,2]

这道题我先用插入排序来做 复杂度O(n^2) ,但遇到了一组数据量很大的数据,TLE了 于是很不情愿的用线段树来解决这个问题
代码:
class mytree{      public:      int start,end;      int count;      mytree* left,*right;      mytree(int start,int end)      {          this->start=start;          this->end=end;          this->left=this->right=nullptr;          this->count=0;      }  };  class Solution {public:   /**     * @param A: An integer array     * @return: Count the number of element before this element 'ai' is      *          smaller than it and return count number array     */     mytree* build(int start,int end)       {           if(start==end)           {               mytree *t1=new mytree(start,end);              return t1;           }           else            {               mytree *t1=new mytree(start,end);               int mid=(start+end)/2;               t1->left=build(start,mid);               t1->right=build(mid+1,end);               return t1;           }       }      void insert(mytree*&t,int index)     {                  if(t->start==t->end&&t->start==index)         (t->count)++;         else         {             int mid=(t->start+t->end)/2;             if(index>mid)insert(t->right,index);             else if(index<=mid)insert(t->left,index);             t->count=t->left->count+t->right->count;                      }              }     int query(mytree*t,int start,int end)     {         if(start>end)return 0;         if(start<=t->start&&end>=t->end)return t->count;         else         {             int mid=(t->start+t->end)/2;             if(mid<start)return query(t->right,start,end);             else if(mid>=end)return query(t->left,start,end);             else return query(t->left,start,mid)+query(t->right,mid+1,end);                      }     }    vector<int> countOfSmallerNumberII(vector<int> &A) {        // write your code here       if(A.size()==0)return vector<int>{};        vector<int>ret;       mytree*t=build(0,10005);       for(int i=0;i<A.size();i++)       {           if(A[i]==0)           {               ret.push_back(0);               insert(t,0);           }           else{           ret.push_back(query(t,0,A[i]-1));                      insert(t,A[i]);           }       }       return ret;        }};








原创粉丝点击