算法与数据结构——树

基本概念

1、有根树

0
1
2
3
4
5
6
7
8
9
10
11
12

如上图所示:圆圈代表结点,连接结点的线代表边。树由结点和连接结点的边组成。
2、有根结点的父子关系:如图根结点为0,连接根结点0到结点4最后一条边连接着结点1和4,则结点1称为结点4的父节点,结点4 为结点1 的子节点。结点2,3为结点4的兄弟结点。
3、根结点:没有父节点的结点(唯一)
外部结点(叶结点):没有子节点的结点(如4,5)
内部结点:除叶节点以外的结点

结点度、高、深度

1、结点的度:一个结点拥有的子节点总数称为结点的度(如节点2,有三个子节点,则节点2的度为3);
2、结点的深度:根结点到某一节点(x)的路径的长度称为 结点x 的深度(如节点2,根结点到2 只经过一条边,则x的深度为1);
3、结点的高:某一节点(x)到子结点的最大路经长称为结点x的高(如节点2,到子节点11(12)路径最长,高度位2)
一颗数的根结点的高度最大,(如节点8,其深度为2,高为1,树高为3)


二叉树

1、概念:拥有一个根结点,且其他结点的子结点数都不超过2。
2、有序树:在二叉树中,每个结点的子节点数不超过2个,而且还分左节点和右节点,像这种子节点有特定顺序的树称为有序树。

// 二叉树的表达
//输入结点个数N,每个结点信息,id(结点编号)left(左子结点编号)
right(右子结点编号。
//输出node:id :parent = p,sibling = s,degree = deg (度)
,depth = dep(深度),height = h(高),
type(结点类型(根,叶或者内结点)
#include <iostream>
#define MAX 10000
#define NIL -1
using namespace std;
typedef struct Node
{
    int parent;
    int left;
    int right;
}Node;

Node T[MAX];
int N,D[MAX],H[MAX];

int getHeight(int u)
{
    int h1 = 0,h2 = 0;
    if(T[u].left != NIL)
    {
        h1 = getHeight(T[u].left)+1;
    }
    if(T[u].right != NIL)
    {
        h2 = getHeight(T[u].right)+1;
    }
    H[u] = max(h1,h2);
    return H[u];
}

void getDepth(int u, int d)
{
    D[u] = d;
    if(T[u].left!= NIL)
    {
        getDepth(T[u].left,d+1);
    }
    if(T[u].right!= NIL)
    {
        getDepth(T[u].right,d);
    }
}
int getSibling(int u)
{
    if(T[u].parent == NIL)
    {
        return NIL;
    }
    if(T[T[u].parent].left!=u && T[T[u].parent].left!=NIL)
    {
        return T[T[u].parent].left;
    }
    if(T[T[u].parent].right!=u && T[T[u].parent].right!=NIL)
    {
        return T[T[u].parent].right;
    }
    return NIL;
}

void Print(int u)
{

    cout<<"node:"<<u;
    cout<<"parent="<<T[u].parent;
    cout<<"sibling="<<getSibling(u);


    int degree = 0;
    if(T[u].left != NIL) degree++;
    if(T[u].right != NIL) degree++;
    cout<<"degree = "<<degree<<",";
    cout<<"depth = "<<D[u]<<",";
    cout<<"height = "<<H[u]<<",";

    if(T[u].parent == NIL) cout<<"root"<<endl;
    else if(T[u].left == NIL && T[u].right == NIL) cout<<"leaf"<<endl;
    else cout<<"internal node"<<endl;
    //int c;
    /*int node= T[u].left;
    cout<<"[";
    for(int i=0;node!=NIL;i++)
    {
        if(i) cout<<", ";
        cout<<node;
        node=T[u].right;
    }
    cout<<"]"<<endl;*/
}

int main()
{
    int v,l,r,root=0;
    cout<<"请输入结点个数:";
    cin>>N;
    for(int i=0;i<N;i++)
    {
        T[i].parent = NIL;
        T[i].left = NIL;
        T[i].right = NIL;
    }

    for(int i=0;i<N;i++)
    {
        cout<<"请输入各结点信息:";
        cin>>v>>l>>r;
        T[v].left = l;
        T[v].right = r;
        if(l!=NIL) T[l].parent = v;
        if(r != NIL) T[r].parent = v;
    }
    for(int i=0;i<N;i++)
    {
        if(T[i].parent == NIL)
        root = i;
    }
    getDepth(root,0);
    getHeight(root);
    for(int i=0;i<N;i++)
    {
        Print(i);
    }

    return 0;
}

树的遍历

1、前序遍历:按照根结点、左子树、右子树的顺序输出结点编号。
下面展示一些 内联代码片

// 前序,先访问u,打印出结点u,然后执行preParse(T[u].left)
访问u的左子树,处理完毕再执行preParse(T[u].right)访问u的右子树,
u=NIL时没有后续结点,函数结束。其他两种只是打印顺序的调整
void preParse(int u)
{
    if(u == NIL)
        return;
    cout<<u<<" ";
    preParse(T[u].left);
    preParse(T[u].right);
}

2、中序遍历:按照左子树、根结点、右子树的顺序输出结点编号。

void inParse(int u)
{
    if(u == NIL)
    {
        return;
    }
    inParse(T[u].left);
    cout<<u<<" ";
    inParse(T[u].right);
}

3、后续遍历:按照左子树、右子树、根结点的顺序输出结点编号。

void postParse(int u)
{
    if(u == NIL)
    {
        return;
    }
    postParse(T[u].left);
    postParse(T[u].right);
    cout<<u<<" ";
}
树的重建

比如当我们知道一个树的前序排列,中序排列,让我们来求这个数的后序排列,这其实就是一种树的重建。
下面这段代码就是根据已知的前序,中序排列,求树的后序排列。

//前序排列为1 2 3 4 5 6 7 8 9
//中序排列为3 2 5 4 6 1 8 7 9
//先遍历前序排列,当前结点为c,然后可以通过查询结点c在in中的位置m,
m的左边就是c的左子树,右边就是c的右子树,然后一步一步找下去。
void reconstruction(int left,int right)
{
    if (left>=right)
    {
        return;
    }
    int root = pre(pos++);
    int m = distance(in.begin(),find(in.begin(),in.end(),root));
    reconstruction(left,m);
    reconstruction(m+1,right);
    post.push_back(root);
}

搜索

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值