二叉树和孩子兄弟树及南航专硕学硕考研编程真题 12年-18年(树专题)

本文深入探讨了树结构的实现与应用,包括孩子兄弟树和二叉树的构建、遍历、高度计算、宽度查找等核心算法。通过实例讲解,帮助读者理解树的性质和操作,适用于算法学习和数据结构课程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

具体思想参照注释

#include<bits/stdc++.h>
using namespace std;
#define maxn 100 

//*********孩子兄弟树********* 
typedef struct CSNode{
    int data;
    CSNode *firstchild;
    CSNode *nextsibling;
}* CSTree;
//第一种递归建立孩子兄弟树
void SetupCSTree(CSTree &head)
{
    int a,b;
    CSTree child,brother; 
    printf("请输入%d的孩子(-1表示空):",head->data);
    scanf("%d",&a);
    printf("请输入%d的兄弟(-1表示空):",head->data);
    scanf("%d",&b);
    if(a == -1)
        head->firstchild = NULL;
    else
    {
        child = (CSTree)malloc(sizeof(CSNode));
        child->data = a;
        head->firstchild = child;
        SetupCSTree(head->firstchild);
    }
    if(b == -1)
        head->nextsibling = NULL;
    else
    {
        brother = (CSTree)malloc(sizeof(CSNode));
        brother->data = b;
        head->nextsibling = brother;
        SetupCSTree(head->nextsibling);
    }
}

void Init_CSTree(CSTree &tree)
{
    int root;
    printf("请输入根节点:");
    scanf("%d",&root);
    tree = (CSTree)malloc(sizeof(CSNode));
    tree->data = root;
    tree->firstchild = NULL;
    tree->nextsibling = NULL;
    SetupCSTree(tree);
}

/*
//第二种递归建立孩子兄弟树的方式 

vector<vector<int> > cstree(maxn);//用来存储孩子兄弟树,然后再链式建树 
int fa[maxn];//用来找某个节点的父亲节点 

CSTree CreateCSTree(int u, int pos) 
{
    // 这个函数比较NB,膜拜王大师,所以我注释详细一些 
    //    u代表当前节点的值  pos代表该节点在它的父亲节点的存放位置 
    //    首先递归过程是首先递归到叶子结点,赋值,返回的过程判断该节点是否有兄弟,
    //    有的话就去搞它的兄弟节点的所有信息 最终构建成功 
    CSTree csnode = (CSTree)malloc(sizeof(CSNode)); //开辟空间 
    if (!cstree[u].empty()) csnode->firstchild = CreateCSTree(cstree[u][0], 0);//搞第一个儿子 
    else csnode->firstchild = NULL;        
    //有兄弟,先去搞兄弟 
    if (u != 1 && pos != cstree[fa[u]].size()-1) csnode->nextsibling = CreateCSTree(cstree[fa[u]][pos+1],pos+1);
    else csnode->nextsibling = NULL;
    csnode->data = u;
    return csnode;
}

//构建孩子兄弟树 -1表示叶子结束  -2表示根节点结束 规定 根必须从1开始 
 //一切为了简化操作  节点值不要重复 
 //比如 先输入根1 然后输入1的孩子 2 3 4 输入-1 换下一个根节点
 // 输入2 输入2的孩子5 输入-1  退出根输入-2 
 
void SetupCSTree(CSTree &head)
{
    char ch;
    int root = 0,leaf = 0;
    while(root != -2){
        printf("请输入根节点(-2表示结束):");
        scanf("%d",&root);
        ch = getchar();
        if(root == -2)
            break;
        while(leaf != -1)
        {
            printf("请输入%d的孩子节点(-1表示结束):",root);
            scanf("%d",&leaf);
            ch = getchar();
            if(leaf == -1)
                break;
            cstree[root].push_back(leaf);
            fa[leaf] = root;
        }
        leaf = 0;
    }    
    head = CreateCSTree(1,0);

*/

//************真题分界线**************** 

int cstree_high = 1; //孩子兄弟树的高度 
int num = 0;//最后一代的个数 
void DFS1(CSTree tree,int high)
{
    if(tree == NULL)
        return;
    if(tree->nextsibling != NULL)
    {
        cstree_high = max(cstree_high,high);    //兄弟 高度不能变 
        DFS1(tree->nextsibling,high);    
    }
    if(tree->firstchild != NULL)//孩子节点不为空 未访问过 
    {
        cstree_high = max(cstree_high,high+1);//高度加一 去最大值 
        DFS1(tree->firstchild,high+1);        
    }

void DFS2(CSTree tree,int high)
{
    if(tree == NULL)
        return;
    else if(high == cstree_high)
    {
        num++;
        printf("%d ",tree->data);
    }
    if(tree->nextsibling != NULL)
    {
        DFS2(tree->nextsibling,high);    
    }
    if(tree->firstchild != NULL)//孩子节点不为空 未访问过 
    {
        DFS2(tree->firstchild,high+1);        
    }
}

void HomeTree(CSTree tree)// 2014年829和922真题  求共有多少代人,最后一代的人数并输出 
{
    /*
        算法思想:第一次dfs搜索,找出共有多少代。 
        注意的是左边是孩子节点,代数要加一,右边是兄弟节点,代数不变 。
        第二次搜索,找到最后一代,输出并计数 
    */ 
    DFS1(tree,1);//第一次搜索 找出共有多少代人
    printf("家谱中共有%d代\n",cstree_high);
    DFS2(tree,1);//总结最后一代人数和输出最后一代 
    printf("\n最后一代人个数是%d",num);
}

int CSTree_width(CSTree tree) //2012年922真题 求孩子兄弟树的宽度 
{
    //算法思想: 层次遍历,每一层找出最大宽度
    if(tree == NULL)// 为空返回0 
        return 0;
    int width = 1;
    int width_tmp = 0;
    queue<CSTree > Q,Assist;  //Q队列存孩子 Assist队列存兄弟 
    CSTree tmp; //队列头
    Q.push(tree);
    while(!Q.empty())
    {
        width_tmp = 0; 
        int length = Q.size();    
        width_tmp = length;    
        while(length--) //所有兄弟节点入队 
        {
            tmp = Q.front();
            if(tmp->firstchild != NULL)
            {
                Q.push(tmp->firstchild);
            }
            while(tmp->nextsibling != NULL)
            {
                Assist.push(tmp->nextsibling);
                tmp = tmp->nextsibling;
            }    
            Q.pop();
        }
        width_tmp += Assist.size(); //该层的宽度
        //清空辅助队列Assist
        while(!Assist.empty())
        {
            tmp = Assist.front();
            if(tmp->firstchild != NULL)
                Q.push(tmp->firstchild);
            Assist.pop(); 
        }
        width = max(width,width_tmp);    
    } 
    return width;
}

//*******二叉树分界线************** 


// 二叉树的节点值是int类型 -1表示该节点为空 
typedef struct BinaryTree{ // 孩子表示 
    int val;
    BinaryTree *left;
    BinaryTree *right; 
}* Tree;

void SetupTree(Tree &tree) //构建一棵二叉树 
{
    int ch;
    char c;
    scanf("%d",&ch);
    c = getchar();//必须清除换行符 
    if(ch == -1)
        tree = NULL;
    else
    {
        tree = (Tree)malloc(sizeof(BinaryTree));
        tree->val = ch;
        printf("输入%d的左子节点:", ch);
        SetupTree(tree->left);
        printf("输入%d的右子节点:", ch);
        SetupTree(tree->right);
    }
}

Tree init_tree(vector<int > &nums,int l,int r) // 递归建立平衡二叉树,根节点就是中间节点 
{
    Tree root = (Tree)malloc(sizeof(BinaryTree));
    if(l <= r)
    {
        int mid = l + (r-l)/2;
        root->val = nums[mid];
        root->left = init_tree(nums,l,mid-1);
        root->right = init_tree(nums,mid+1,r);
    }
    else
        return NULL;
    return root;
}
 
void SetupBinarySortTree(Tree &tree)// 给一个有序数组,建立成二叉排序树 
{
    int n;
    printf("请输入数组元素的个数:");
    scanf("%d",&n);
    vector<int > array(n);
    for(int i = 0;i < n;i++)
        scanf("%d",&array[i]);
    sort(array.begin(),array.end()); //数组排序
    if(array.size() == 0) { tree = NULL; return; }
    tree = init_tree(array,0,array.size()-1); //递归建树 
}
 
bool DestroyTree(Tree &tree) //销毁一颗树 
{
    if(tree != NULL)
    {
        DestroyTree(tree->left);
        DestroyTree(tree->right);
        free(tree);
        tree = NULL; //这一步不可少,不然指针浮空 不是自动指向NULL 
    }
    return true;
}

void Recursive_Preorder(Tree tree)//递归先序
{
    if(tree != NULL)
    {
        printf("%d ",tree->val);
        Recursive_Preorder(tree->left);
        Recursive_Preorder(tree->right);    
    }    

void Recursive_Inorder(Tree tree)//递归中序 
{
    if(tree != NULL)
    {
        Recursive_Inorder(tree->left);
        printf("%d ",tree->val);
        Recursive_Inorder(tree->right);    
    }    

void Recursive_Postorder(Tree tree)//递归后序 
{
    if(tree != NULL)
    {
        Recursive_Postorder(tree->left);
        Recursive_Postorder(tree->right);
        printf("%d ",tree->val);    
    }    

int Recursive_TreeHigh(Tree tree)//递归求树的高度 
{
    int high = 0;
    if(tree == NULL)
        high = 0; 
    else
        high = max(Recursive_TreeHigh(tree->left),Recursive_TreeHigh(tree->right) ) + 1; 
    return high;
}

int TreeHigh(Tree tree)//2013年829和922真题 非递归求树的高度 
{
    /*
        算法思想,层次遍历,用队列存储  
    */
    if(tree == NULL)
        return 0;
    queue<pair<Tree,int> >  Q;//定义一个队列 队列元素是树的节点和它的高度 用pair组合
    pair<Tree,int> tmp_tree;//临时节点 
    Q.push(make_pair(tree,1));
    int high = 1;//结果高度 
    while(!Q.empty())//队列不为空
    {
        tmp_tree = Q.front();//取出队列头结点
        if(tmp_tree.first->left != NULL)
        {
            Q.push(make_pair(tmp_tree.first->left,tmp_tree.second+1));    //左子树入队 高度+1 
        }
        if(tmp_tree.first->right != NULL)
        {
            Q.push(make_pair(tmp_tree.first->right,tmp_tree.second+1));//右子树入队 高度+1 
        }    
        high = max(high,tmp_tree.second);
        Q.pop(); 
    } 
    return high;
    
    //不会用STL的pair的 换一个写法 
/*
    思想:用一个变量控制入队的每一层的个数 然后根据这个控制出队的数量 
    if(tree == NULL)
        return 0;
    queue<Tree > Q;
    Tree tmp; //表示队列头 
    Q.push(tree);
    int high = 0; //高度 
    int queue_size = 0; //用来控制一个队列中如何表示一层的元素
    while(!Q.empty())
    {
        high++; //每次开始都是下一层节点 
        queue_size =  Q.size(); //求出当前队列元素个数 
        while(queue_size--) //循环使队列中当前层节点清空 加入下一层节点
        {
            tmp = Q.front();
            if(tmp->left != NULL)
            {
                Q.push(tmp->left);    
            } 
            if(tmp->right != NULL)
            {
                Q.push(tmp->right);    
            } 
            Q.pop();
        }
    } 
    return high;
*/
}

void LongestPath_first(Tree tree) //2015年829和922真题  输出树的最长路径 
{
    /*
        算法思想:题目没要求时间空间 所以暴力求解
        思路:写一个求树的深度函数 每次找最高的分支 
    */
    if(tree != NULL)
    {
        printf("%d ",tree->val);
        int left = Recursive_TreeHigh(tree->left);
        int right = Recursive_TreeHigh(tree->right);
        if(left > right)
            LongestPath_first(tree->left);
        else
            LongestPath_first(tree->right);
    }
}

bool Complete_BinaryTree(Tree tree)//2016年829和922真题 判断是否是完全二叉树 
{
    /*
        如果树为空,则直接返回错 
        如果树不为空:层序遍历二叉树 
        如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列; 
        如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树; 
        如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空;
        则该节点之后的队列中的结点都为叶子节点;
        该树才是完全二叉树,否则就不是完全二叉树;
    */
    if(tree == NULL)//树为空 不成立 
        return false;
    bool flag = false; //用于判断叶子结点 
    queue<Tree > Q;//队列 
    Tree tmp; //用于保存队头 
    Q.push(tree);
    while(!Q.empty())
    {
        tmp = Q.front();
        if(tmp->left && tmp->right)//左右不为空
        {
            Q.pop();
            Q.push(tmp->left);
            Q.push(tmp->right);//左右子树入队 
        } 
        if(tmp->left == NULL && tmp->right != NULL) //左空右不空 必不是完全二叉树 
            return false;
        //左不空右空 或者 左右全空 表示后面全是叶子结点,否则必不是完全二叉树 
        if( (tmp->left != NULL && tmp->right == NULL) || (tmp->left == NULL && tmp->right == NULL)) 
        {
            Q.pop();
            while(!Q.empty())
            {
                tmp = Q.front();
                if(tmp->left != NULL || tmp->right != NULL)//不是叶子节点 不是完全二叉树 
                    return false;
                Q.pop();
            }
            return true; //队列为空 全是叶子节点,则是完全二叉树 
        }
    } 
    return true;
}

int TwoElem() //2018年829和922真题  判断两个元素和树中元素的关系 
{
    Tree tree;
    printf("请输入根节点:");
    SetupTree(tree);
    int a,b;
    int high_a = 0;
    int high_b = 0;
    scanf("%d",&a); 
    scanf("%d",&b); //输入两个整数
    if(tree == NULL) 
        return -2;
    queue<pair<Tree,int> > Q;
    pair<Tree,int> tmp; //保存队头结点 
    Q.push(make_pair(tree,1));
    while(!Q.empty())
    {
        tmp = Q.front();
        if(tmp.first->val == a)
            high_a = tmp.second;
        if(tmp.first->val == b)
            high_b = tmp.second;
        if(tmp.first->left != NULL)
            Q.push(make_pair(tmp.first->left,tmp.second+1));
        if(tmp.first->right != NULL)
            Q.push(make_pair(tmp.first->right,tmp.second+1));
        Q.pop();            
    } 
    if(high_a == 0 && high_b == 0) //都没找到 
        return -2;
    if(high_a != 0 && high_b != 0) //都找到了 
        return abs(high_a-high_b);
    else //找到一个 
        return -1;
}

void Delete_X(Tree &tree,int x) //先序遍历  删除以x为根的子树
{
    if(tree != NULL)
    {
        if(tree->val == x) 
        {
            DestroyTree(tree);
            return ;   //这一句不可少 
        }
        Delete_X(tree->left,x);
        Delete_X(tree->right,x);
    } 
}

void Delete_elem()//2017年922真题 对于给定的值x 删除以x为根的子树
{
    int x;
    Tree tree;
    printf("请输入根节点:");
    SetupTree(tree);
    printf("请输入值x:");
    scanf("%d",&x);
    printf("输出未删除%d先序遍历的结果:\n",x);
    Recursive_Preorder(tree);
    Delete_X(tree,x); //建好树 调用删除函数 
    printf("输出删除%d后先序遍历的结果:\n",x);
    Recursive_Preorder(tree);

Tree Lower_boundTree() //2012年922真题 在平衡二叉树中找到小于x且最接近x的节点返回指针
{
    //算法思想 如果当前节点大于x,去左子树找,如果小于x,则需要到右子树去找
    // 如果右子树的值大于x说明当前节点就是最接近x且小于x,否当前节点跳到右子树 
    int x;
    Tree tree;
    SetupBinarySortTree(tree);
    printf("请输入查找到值x:");
    scanf("%d",&x);
    if(tree == NULL)//树为空 返回空 
        return NULL;  
    Tree treenode = tree; 
    while(treenode != NULL)
    {
        if(treenode->val >= x) //当前节点大于x 
        {                        
            if(treenode->left != NULL)
                treenode = treenode->left; //到左子树 
            else
                break;  //不存在 返回空 
        }
        else if(treenode->val < x) //注意 不要直接else 因为不能找到等于x的
        {    
            if(treenode->right != NULL)
            {
                if(treenode->right->val > x) //右子树节点大于x 则返回当前节点 
                    return treenode; 
                else if(treenode->right->val < x) //继续向右搜索 
                    treenode = treenode->right;
            } 
        }
    }
    return NULL;

 
int main()
{
    //Tree tree;
    //printf("请输入根节点:");
    //SetupTree(tree);
    //Recursive_Preorder(tree);
    //Recursive_Inorder(tree);
    //Recursive_Postorder(tree);
    //Recursive_TreeHigh(tree);
    //printf("%d",TreeHigh(tree));
    //LongestPath_first(tree);
    //cout<<Complete_BinaryTree(tree)<<endl;
   /*    CSTree tree;
    SetupCSTree(tree);
    HomeTree(tree);*/
//    printf("%d",TwoElem() );
    //Delete_elem();
    //SetupBinarySortTree(tree);
    //tree = Lower_boundTree();
    //if(tree)
    //    printf("%d\n",tree->val);
    //else
    //    printf("-1\n");
    CSTree tree;
    Init_CSTree(tree);
    printf("%d\n",CSTree_width(tree));
    return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值