二叉树的存储结构:
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
通过结构体来实现:
tree
,tree[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.单点修改 区间查询
线段树的查询方法:
如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
代码实现:
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;
}