数据结构------二叉树总结(BST+Treap+ 线段树+持续更新

二叉树的存储结构:

struct node{
    int value;
    node *l,*r;
};

             使用以后要delete释放内存,防止内存泄漏

二叉树的遍历:

        1.宽度优先遍历

                        使用队列queue实现搜索        

        2.深度优先遍历

                2.1先序遍历

void preorder(node *root){
    cout<<root->value;
    preorder(root->l);
    preorder(root->r);
}

                2.2中序遍历

void inorder(node *root){
    
    inorder(root->l);
    cout<<root->value;
    intorder(root->r);
}

                2.3后序遍历

void postorder(node *root){
    postorder(root->l);
    postorder(root->r);
    cout<<root->value;
}

我们可以通过前序遍历 中序遍历从而建树 得到后序遍历的结果

思路就是通过前序遍历找到根节点

然后结合中序遍历找到这个点左面的就是左子树上的 对应右边的数就是右子树上的

然后递归解决

下面附带题目练习:

        Binary Tree Traversals(二叉树)_m0_57006708的博客-CSDN博客

二叉搜索树(BST)

特征:任何一个结点的值都比他左子树结点的值大,比右子树结点的值小

 (1)建树和插入 

                        插入从根节点开始如果数据y比根节点x小 则插入到左子树否则插入到右子树,如果                                  子树为空那么就直接放到这个空位上去

(2)查询

                        从根节点开始的递归过程

(3)删除

                        删除一个结点x以后仍然是一个BST树,先找到结点x,如果位于最底层的叶子结点,那么直接删除;如果x只有左子树或者只有右子树,直接删除x,原来的位置由R或者L来代替如果左右子树都有的话,x位置需要重新建树 :先搜索x左子树的最大值y移到x的位置,这相当于以y为根节点的子树删除了y,然后继续对y的左子树进行类似操作,是一个递归过程。

BST算法有 AVL树 红黑树 Splay树 Treap树 SBT树

Treap树 (平衡二叉搜索树)

 每个结点的优先级互不相等,形态是唯一的

例题附上

Shaolin (Treap树)_m0_57006708的博客-CSDN博客

线段树:

   作用:优化区间修改和维护

   性质 :点的权值为lson+rson

    通过结构体来实现:

        treetree[i].l和tree[i].r分别表示这个点代表的线段的左右下标,tree[i].sum表示这个节点表示的线段和

   代码实现建树:

inline void build(int i,int l,int r){//递归建树
    tree[i].l=l;tree[i].r=r;
    if(l==r){//如果这个节点是叶子节点
        tree[i].sum=input[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(i*2,l,mid);//分别构造左子树和右子树
    build(i*2+1,mid+1,r);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//刚才我们发现的性质return ;
}

1.单点修改 区间查询        

                线段树的查询方法:

  1. 如果这个区间被完全包括在目标区间里面,直接返回这个区间的值

  2. 如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

  3. 如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

                        代码实现:

inline int search(int i,int l,int r){
    if(tree[i].l>=l && tree[i].r<=r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
        return tree[i].sum;
    if(tree[i].r<l || tree[i].l>r)  return 0;//如果这个区间和目标区间毫不相干,返回0
    int s=0;
    if(tree[i*2].r>=l)  s+=search(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
    if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
    return s;
}

                    修改区间单点:

                        从根节点开始 先找点,看这点在lson还是rson上然后返回的时候注意还要维护更新 tree[i].sum=tree[i*2].sum+tree[i*2+1].sum所有路过的点

                        代码实现 :

                                      

inline void add(int i,int dis,int k){
    if(tree[i].l==tree[i].r){//如果是叶子节点,那么说明找到了
        tree[i].sum+=k;
        return ;
    }
    if(dis<=tree[i*2].r)  add(i*2,dis,k);//在哪往哪跑
    else  add(i*2+1,dis,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//返回更新
    return ;
}

2.区间修改 单点查询:

                思路:比如我们要把一个l到r区间的数都加k,那么我们只需要在这个区间上做一个k的标记 然后单点查询的时候只需要 从上到下把标记加上就行

                  标记的代码如下:

inline void add(int i,int l,int r,int k){
    if(tree[i].l>=l && tree[i].r<=r){//如果这个区间被完全包括在目标区间里面,讲这个区间标记k
        tree[i].sum+=k;
        return ;
    }
    if(tree[i*2].r>=l)
        add(i*2,l,r,k);
    if(tree[i*2+1].l<=r)
        add(i*2+1,l,r,k);
}

                单点查询的代码:从上到下 把沿路的标记全部加上

void search(int i,int dis){
    ans+=tree[i].sum;//一路加起来
    if(tree[i].l==tree[i].r)
        return ;
    if(dis<=tree[i*2].r)
        search(i*2,dis);
    if(dis>=tree[i*2+1].l)
        search(i*2+1,dis);
}

进阶线段树(pushdown精髓):

        区间修改的原则:

1、如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)

2、如果没有完全覆盖,则先下传懒标记

3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

                代码实现:

void add(int i,int l,int r,int k)
{
    if(tree[i].r<=r && tree[i].l>=l)//如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)
    {
        tree[i].sum+=k*(tree[i].r-tree[i].l+1);
        tree[i].lz+=k;//记录lazytage
        return ;
    }
    push_down(i);//向下传递
    if(tree[i*2].r>=l)
        add(i*2,l,r,k);
    if(tree[i*2+1].l<=r)
        add(i*2+1,l,r,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    return ;
}

                 pushdown的作用是:把自己的懒标记归零,加给自己的儿子,并让自己的儿子加上k*(r-l+1) 

                        代码实现:

void push_down(int i)
{
    if(tree[i].lz!=0)
    {
        tree[i*2].lz+=tree[i].lz;//左右儿子分别加上父亲的lz
        tree[i*2+1].lz+=tree[i].lz;
        int mid=(tree[i].l+tree[i].r)/2;
        tree[i*2].sum+=tree[i].lz*(mid-tree[i*2].l+1);//左右分别求和加起来
        tree[i*2+1].sum+=tree[i].lz*(tree[i*2+1].r-mid);
        tree[i].lz=0;//父亲lz归零
    }
    return ;
}

        查询时候 如果区间没有全部包含于搜索区间 那么要将懒标记下传

代码如下:

inline int search(int i,int l,int r){
    if(tree[i].l>=l && tree[i].r<=r)
        return tree[i].sum;
    if(tree[i].r<l || tree[i].l>r)  return 0;
    push_down(i);
    int s=0;
    if(tree[i*2].r>=l)  s+=search(i*2,l,r);
    if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);
    return s;
}

猜你喜欢

转载自blog.csdn.net/m0_57006708/article/details/120402791