数据结构学习记录——二叉树

1.简介

树是一种非线性数据结构,相较于队列,链表,树的一个节点可能会生出多个分支。

一般而言,一个树会包含一个根节点,向下延伸出若干个子节点,每个末端的节点被称为叶子节点

2.有根树

有几个常见的概念:树的根节点,父亲节点,兄弟节点,子节点,祖先,后代(子树),树深度h,节点深度hp。

2.1二叉树

对于一个节点,他拥有的子结点个数成为这个节点的度。节点的度不超过2的树成为二叉树。

两个子节点被称为左右儿子,子树被称为左右子树。

左右儿子甚至根节点本身都有可能缺失(一个节点都没有称为空二叉树)

有相同节点深度的二叉树节点处于同一层。

完全二叉树:除了最后一层外其他层的节点数全满,而且最后一层的节点从左到右排满除直到最后一个节点。

 满二叉树:所有层的节点全满。

2.1.1 完全二叉树的存储

完全二叉树由于特性,可以简单地用数组来模拟其结构:

        一般会以数组 [1] 位置为根节点建立二叉树;

        数组的 [t] 位置的左儿子和右儿子对应的位置分别为 [2t] 和 [2t+1] ,父亲节点的位置为 [t/2] 。

void Build(int t){
    updataData(t);//添加数据
    Build(t+t);
    Build(2t+1);//如果子节点存在
    

           用这种方法建立非完全二叉树,由于需要保证每个子节点的标号是父亲节点的2倍,因此会浪费掉大量的空间。

2.1.2 一般二叉树的存储

可以用数组下标模拟节点编号,用多个数组来记录节点信息。

        fa[N], L[N], R[N]

struct TreeNode{
    int value;
    int l, r, fa;
} a[100001];
//用struct存储

  与链表类似,二叉树的父节点、子节点信息也可以用指针来存,同时我们会记录根节点指针。

struct TreeNode{
    int value;
    TreeNode *l, *r, *fa;
};

TreeNode *root;

2.1.3二叉树基本操作

//新建节点
struct TreeNode{
    int value;
    TreeNode *l, *r, *fa;//初始为NULL
    TreeNode(int x){value = x;}
};

TreeNode *p = new TreeNode(x);

//根节点初始化
TreeNode *root;
root = new TreeNode(v);

//插入子节点
void Insert(TreeNode *fa, TreeNode *p, int flag){
    //flag = 0插入左边,=1插入右边
    if(!flag)
        fa->l = p;
    else
        fa->r = p;
    p->fa = fa;
}

TreeNode *p = new TreeNode(v);
Insert(fa, p, flag);

2.1.4 二叉树的遍历顺序

遍历顺序可分为:先序遍历,中序遍历,后序遍历

//先序遍历(DLR)
void PreOrder(TreeNode *p){
    cout << p->value << endl;
    if(p->l)PreOrder(p->l);
    if(p->r)PreOrder(p->r);
}

PreOrder(root);
//中序遍历(LDR)
void InOrder(TreeNode *p){
    if(p->l)InOrder(p->l);
    cout << p->value << endl;
    if(p->r)InOrder(p->r);
}

InOrder(root);
//后序遍历(LRD)
void PostOrder(TreeNode *p){
    if(p->l)InOrder(p->l);
    if(p->r)InOrder(p->r);
    cout << p->value << endl;
}

InOrder(root);
//层级遍历(BFS序列)
TreeNode *q[N];

void Bfs(TreeNode *root){
    int front = 1, rear = 1;
    q[1] = root; 
    while(front <= rear){
        TreeNode *p = q[front];
        front++;
        cout << p->value << endl;
        if(p->l) q[++rear] = p->l;
        if(p->r) q[++rear] = p->r;
    }
}

Bfs(root);

例题1. 遍历一般二叉树

给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。请你求出这棵二叉树的先序、中序和后序遍历的结果。

输入格式

        第一行一个整数 n 表示节点数。接下来 n 行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0 表示没有对应的子节点。

        输入保证是一棵二叉树。

输出格式

        输出三行,每行 n 个数代表一种遍历的结果。

        第一行为先序遍历的结果,第二行为中序遍历的结果,第三行为后序遍历的结果。

数据规模

        对于所有数据,保证 1≤n≤1024。

#include<bits/stdc++.h>;

using namespace std;

int n;
struct TreeNode{
    int fa;
    int l, r;
} a[1025];

inline void preorder(int t){
    printf("%d ", t);
    if(a[t].l)
        preorder(a[t].l);
    if(a[t].r)
        preorder(a[t].r);
}

inline void inorder(int t){
    if(a[t].l)
        inorder(a[t].l);
    printf("%d ", t);
    if(a[t].r)
        inorder(a[t].r);
}

inline void postorder(int t){
    if(a[t].l)
        postorder(a[t].l);
    if(a[t].r)
        postorder(a[t].r);
    printf("%d ", t);
}

int main(){
    scanf("%d",&n);
    for(int i = 1; i<= n; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        if(x)
            a[i].l = x, a[x].fa = i;
        if(y)
            a[i].r = y, a[y].fa = i;
    }
    preorder(1);
    printf("\n");
    inorder(1);
    printf("\n");
    postorder(1);
}

例题2.二叉树的最近公共祖先

        给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。读入 u,v ,请求出 u 号节点和 v 号节点的最近公共祖先Lowest Common Ancestor)。

        如果 x 号节点既是 u 号节点的祖先也是 v 号节点的祖先,则称 x 号节点是 u 号节点和 v号节点的公共祖先

        如果 x号节点是 u 号节点和 v 号节点的所有公共祖先中深度最深的,则称 x 号节点是 u 号节点和 v 号节点的最近公共祖先

输入格式

        第一行一个整数 n表示节点数。

        接下来 n行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0表示没有对应的子节点。

        输入保证是一棵二叉树。

最后一行两个整数 u,v表示要求最近公共祖先的两个节点的编号。

输出格式

        输出一行一个整数,代表 u号节点和 v 号节点的最近公共祖先。

数据规模:

对于所有数据,保证 2≤n≤1000,1≤u,v≤n。

#include<bits/stdc++.h>;

using namespace std;

struct node{
    int l, r, fa;
}a[1001];

int n , c[1001], d[1001];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        if(x)
            a[i].l = x, a[x].fa = i;
        if(y)
            a[i].r = y, a[y].fa = i; 
    }
    int u, v;
    scanf("%d%d", &u, &v);
    int l1 = 0;
    while(u!= 1)
        c[++l1] = u, u = a[u].fa;
    c[++l1] = 1;
    int l2 = 0;
    while(v!= 1)
        d[++l2] = v, v = a[v].fa;
    d[++l2] = 1;
    int x = 0;
    for(int i = l1, j = l2; i && j; --i, --j)
        if(c[i] == d[j])
            x = c[i];
        else
            break;
    printf("%d", x);
}

例题3.二叉树的子树和1

        给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。每个节点都有一个权值,i 号节点的权值为 ai,请求出每个节点的子树的权值和(子树内节点的权值的和)。

输入格式

        第一行一个整数 n表示节点数。接下来 n行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0表示没有对应的子节点。

        输入保证是一棵二叉树。

        接下来一行 n个整数,第 i 个整数 ai 表示 i号节点的权值。

输出格式

        输出一行 n个整数,第 i 个整数表示 i 号节点的子树的权值和。

数据规模

        对于所有数据,保证 1≤n≤1000,1≤ai≤100。

#include<bits/stdc++.h>;

using namespace std;

struct node{
    int l, r, fa;
    int value;
}a[1001];

int n, cnt;


inline void order(int t){
    cnt+= a[t].value;
    if(a[t].l)
        order(a[t].l);
    if(a[t].r)
        order(a[t].r);
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        if(x)
            a[i].l = x, a[x].fa = i;
        if(y)
            a[i].r = y, a[y].fa = i; 
    }
    
    for(int i = 1; i<=n; i++){
        scanf("%d", &a[i].value);
    }

    for(int i = 1; i<=n; i++){
        cnt = 0;
        order(i);
        printf("%d ", cnt);
    }
}    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值