树的基本操作-PAT知识点-必会

树的基本操作-PAT知识点-必会

树的数据结构

二叉树

一般的度为2的树是不考虑左右子树的顺序的,也就是说,左右子树交换对该树没影响。但是!==二叉树是规定左右子树顺序的!==左右子树交换后就是另一个二叉树了!左右子树的交换方法下面会讲,就是在后序遍历的时候交换左右子树。

struct Node
{
    int data;//数据域
    Node *lchild;//左孩子
    Node *rchild;//右孩子
}

一般题目都是给定结点序号,下面的更常用

const int MAXN=1000;//假设最大序号为1000
struct Node
{
    int data;
    int lchild;//存放序号,int型
    int rchild;//存放序号,int型
}node[MAXN];//开辟MAXN个结点来存储树

完全二叉树

完全二叉树指除了最后一层可以不排满(但必须从左到右排)节点,其他层必须满节点的树

性质:如果设根节点的序号为1,那么其左子节点的序号为1*2=2,其由节点的序号为1*2+1=3。只有规定了根节点序号为1,那么对于任意一个序号为n的节点,其左孩子序号为2n,其右孩子序号为2n+1

由于指定序号,必须用静态方式(数组)存储。

const int MAXN=1000;//假设最大序号为1000
struct Node
{
    int data;
    int lchild;//存放序号,int型
    int rchild;//存放序号,int型
}node[MAXN];//开辟MAXN个结点来存储树

普通的树

const int MAXN=1000;
struct Node
{
    int data;
    vector<int> child;//该节点的child的序号全部放入vector数组中
}node[MAXN];
//其实如果这个树的结点不存放其他信息的话,直接写成vector<int> child[MAXN]就行了

树的建立

一般除了二叉查找树是给一系列数值信息,建立树一般都是给定当前节点序号和该节点的孩子的序号。下面就根据这种常见的建立方式编码。

二叉树的建立

int lchild,rchild;
for(int i=0;i<n;i++)//给定n个节点和其左右孩子的信息,假设编号为0到n-1
{
    scanf("%d%d",&lchild,&rchild);
    node[i].lchild=lchild;
    node[i].rchild=rchild;
}

树的建立

int child;
for(int i=0;i<n;i++)//给定n个节点和其左右孩子的信息,假设编号为0到n-1
{
    int num;//该节点的孩子数目
    scanf("%d",&num);
    for(int j=0;j<num;j++)
    {
        scanf("%d",&child);
        node[i].push_back(child);//vector存储孩子编号
    }
}

树的遍历

这里用静态树的遍历说明,思想都是一样的。

这里注意,无论是什么遍历,都是左子树先遍历

前序遍历/深度优先遍历DFS

/*二叉树*/
void preOrder(int root)
{
    if(root==-1)//没有子节点了,直接返回
        return;
    printf("%d",root);//先遍历根节点,这里的遍历操作就是输出它
    preOrder(node[root].lchild);
    preOrder(node[root].rchild);
}

/*一般的树,其实这个就是一般的树的DFS!!!*/
void preOrder(int root)
{
    printf("%d",root);
    int len=node[root].size();//到叶子结点时,len为0,就不会向下遍历了。这里树的表示为vector<int> node[MAXN]
    for(int i=0;i<len;i++)
    {
        preOrder(node[root].child[i]);
    }
}

中序遍历

void inOrder(int root)
{
    if(root==-1)
        return;
    inOrder(node[root].lchild);
    printf("%d",root);
   	inOrder(node[root].rchild);
}

后序遍历

void postOrder(int root)
{
    if(root==-1)
        return;
    inOrder(node[root].lchild);
   	inOrder(node[root].rchild);
    printf("%d",root);
}

层序遍历/广度优先遍历BFS

/*二叉树*/
void levelOrder(int root)
{
    queue<int> q;
    q.push(root);
    while(!q.empty())
    {
        int front=q.front();
        printf("%d",front);//遍历,这里的遍历是输出信息
        q.pop();
        if(node[front].lchild!=-1)
        {
            q.push(node[front].lchild);
        }
        if(node[front].rchild!=-1)
        {
            q.push(node[front].rchild);
        }
    }
}

/*普通的树*/
void levelOrder(int root)
{
    queue<int> q;
    q.push(root);
    while(!q.empty())
    {
        int front=q.front();
        printf("%d",front);
        q.pop();
        int len=node[front].size();//这里树的表示是vector<int> node[MAXN]
        int child;//表示孩子结点的序号
        for(int i=0;i<len;i++)
        {
            child=node[front][i];
            if(node[child]!=-1)
            {
                q.push(child);
            }
        }
    }
}

前序/后续遍历+中序遍历还原二叉树

前序遍历+中序遍历

/*
pre[]:前序遍历序列,存放节点序号
lpre:前序遍历左界
rpre:前序遍历右界
in[]:中序遍历序列,存放节点序号
lin:中序遍历左界
rin:中序遍历右界
*/
void build(int pre[],int lpre,int rpre,int in[],int lin,int rin)
{
    if(lpre>rpre)//序列中没元素了,也就是没子树了,直接返回-1
        return NULL;
    
    Node *root=new Node();
    root->data=pre[lpre];//根节点的序号
    int k;//表示中序序列中根节点的位置
    for(k=lin;k<=rin;k++)//遍历中序序列,找到与前序序列最左端相等的元素,该元素即为根节点。注意<=号
    {
        if(root->data==lin[k])//找到根节点
            return;
    }
 
    int numLeft=k-lin;//左子树节点的个数
    root->lchild=build(pre,lpre+1,lpre+1+numLeft-1,in,lin,k-1);//左子树部分递归,左界x,y个元素,那么右界就为x+y-1
    root->rchild=build(pre,lpre+1+numLeft-1+1,rpre,in,k+1,rin);//右子树部分递归
    return root;
}

后序遍历+中序遍历

/*
post[]:后序遍历序列,存放节点序号
lpost:后序遍历左界
rpost:后序遍历右界
in[]:中序遍历序列,存放节点序号
lin:中序遍历左界
rin:中序遍历右界
*/
void build(int post[],int lpost,int rpost,int in[],int lin,int rin)
{
    if(lpost>rpost)
    	return NULL;
    Node *root=new Node();
    root->data=post[rpost];
    int k;
    for(k=0;k<=rin;k++)
    {
        if(root->data==in[k])
            return;
    }
    int numLeft=k-lin;
    root->lchild=build(post,lpost,rpost-1-numLeft+1-1,in,lin,k-1);
    root->rchild=build(post,rpost-1-numLeft+1,rpost-1,in,k+1,rin);
    return root;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值