王道书 P149 T5(求树高) + 拓展(求某点的层次/深度、求树的宽度)(二叉树链式存储实现)

/**
 * 用二叉树链式存储实现 王道 P149 T5(求树高) + 拓展(求某点的层次/深度、求树的宽度)
 *
 *
 * ①算法思想
 * 注:①是递归求树高(两种方法实现)
 *    ②是非递归求树高(非递归后序遍历拿来改一下)
 *    ③是非递归求层次/深度(非递归后序遍历或②拿来改一下)
 *    ④是递归求层次/深度(递归的先序、中序、后序遍历都可以拿来改一下)
 *    ⑤是递归方式求树高(递归的先序、中序、后序遍历或②都可以拿来改一下)
 *    ⑥是非递归求树的宽度(层次遍历拿来改一下)
 *    ⑦是非递归求树高(层次遍历或⑥拿来改一下)
 * 对于① 递归求树高(平时经常用的)
 * 首先,如果树是空的话,树高就是0;
 * 当树不为空时,高度就是左子树和右子树中较高的那一个的高度再加上根节点自己的高度。
 * 此题就转化成了求左右子树高度的问题。
 * 对于② 非递归求树高
 * 二叉树的高度 = 从根节点到叶子节点的路径长度之和的最大值 + 1,把非递归后序遍历拿来改一下(注意不要着急出栈那一点)。
 * 对于③ 非递归求层次/深度
 * 求某点的层次和②是有相似之处的,把②改一下即可(注意不要着急出栈那一点)。
 * 对于④ 递归求层次/深度
 * 递归的先序、中序、后序遍历都可以拿来改一下。
 * 对于⑤ 递归求树高
 * 其实就是求最大深度,递归的先序、中序、后序遍历或②都可以拿来改一下。
 * 对于⑥ 非递归求树宽
 * 从层次遍历的角度思考。
 * 对于⑦ 非递归求树高
 * 从层次遍历的角度或⑥的角度思考。
 *
 *
 * ②算法设计
 */

#include <stdio.h>
#include <iostream>
#define MaxSize 100

typedef struct BiTreeNode{
    int data;
    BiTreeNode *lchild,*rchild;
}BiTreeNode,*BiTree;


//P149 T5
//①递归算法直接求解二叉树高度(可以动手画个栈一层一层压栈再return弹栈理解一下)
//法1:
int GetHeightNR(BiTree T){
    if(T == NULL)
        return 0;
    int left = GetHeightNR(T -> lchild);
    int right = GetHeightNR(T -> rchild);
    return left > right ? left + 1 : right + 1;
}
//法2:
int height(BiTree T){
    if(T == NULL)
        return 0;
    if(T -> lchild) 
        return 1 + height(T -> lchild);
    if(T -> rchild)
        return 1 + height(T -> rchild);
}
//②非递归算法求解二叉树高度---通过求根节点到叶子结点的路径长度之和最大的值+1的方式求解二叉树高度
int GetHeightByPostNR(BiTree T){
    BiTree stack[MaxSize],p = T;
    int top = -1,tag[MaxSize] = {0},max = -1;//max用来记录高度
    while(p || top != -1){
        if(p){
            stack[++top] = p;
            tag[top] = 1;
            p = p -> lchild;
        }else{
            if(tag[top] == 1){
                tag[top] = 2;
                p = stack[top];//找到栈顶元素,这样才能接着往下找
                p = p -> rchild;
            }else{//说明是狭义的“叶子节点”了
                p = stack[top];//访问这个点的时候先不急着出栈,因为如果直接出栈的话,高度就有损失了
                if(p -> lchild == NULL && p -> rchild == NULL){//说明是叶子节点
                    if(top + 1 > max)//top是从-1开始的,或者说top是数组的下标,所以要+1才是路径的长度
                        //注意这个top是一直在变化的,所以每遇到一个叶子节点,就要来比较一下,这样才能找到路径最长的叶子节点。
                        max = top + 1;
                }
                top--;
                p = NULL;
            }
        }
    }
    return max;
}
//③使用非递归方式通过②可以拓展到求出某个点所在的层次/深度
int GetLevelByPostNR(BiTree T,int data){
    BiTree stack[MaxSize],p = T;
    int top = -1,tag[MaxSize] = {0};
    while(p || top != -1){
        if(p){
            stack[++top] = p;
            tag[top] = 1;
            p = p -> lchild;
        }else{
            if(tag[top] == 1){
                tag[top] = 2;
                p = stack[top];//找到栈顶元素,这样才能接着往下找
                p = p -> rchild;
            }else{//说明是叶子节点了
                p = stack[top];//访问这个点的时候先不急着出栈,因为如果直接出栈的话,高度就有损失了
                if(p -> data == data){
                    return top + 1;//在非递归后序遍历中,每个点其实都是某种意义上的叶子节点,每个要弹出的元素都会经历这一步,
                    //在经历这一步时,都是某种意义上的叶子节点,而此时就和②相似了,
                    //根节点到叶子结点的路径长度+1就是此节点的高度,此时要弹出的这个点就相当于叶子节点,
                    //所以可以用和②同样的方法,算此节点的层次就是路径长度+1,
                    //而路径长度就是前面的祖先数量,也就是在压在栈里还没有弹出的元素数量。
                    //所以就是top+1。
                }
                top--;
                p = NULL;
            }
        }
    }
    return -1;//说明没找到这个点
}
//④递归方式求某点的层次/深度
//刚开始传T,1,4,dataDeep
void GetDeep(BiTree T,int deep,int data,int &dataDeep){//GetDeep中的Deep和level是一个意思,dataDeep是这个data的深度
    if(T != NULL){
        if(T -> data == data){
            dataDeep = deep;//用dataDeep把deep带回
        }
        GetDeep(T -> lchild,deep + 1,data,dataDeep);
        GetDeep(T -> rchild,deep + 1,data,dataDeep);
    }
}
//关于④的测试
int main(){
    BiTree T = CreatBiTree();
    int dataDeep = -1;
    GetDeep(T,1,4,dataDeep);
    printf("%d ",dataDeep);
    return 0;
}
//⑤递归方式求树高(高度其实就是最大深度)
//刚开始传T,1,0
void GetHeightByDeep(BiTree T,int deep,int &height){//用height带回
    if(T != NULL){
        if(T -> lchild == NULL && T -> rchild == NULL){//如果是叶子节点
            if(deep > height)
                height = deep;
        }
        GetHeightByDeep(T -> lchild,deep + 1,height);
        GetHeightByDeep(T -> rchild,deep + 1,height);
    }
}
//关于⑤的测试
int main(){
    BiTree T = CreatBiTree();
    int height = 0;
    GetHeightByDeep(T,1,height);
    printf("%d ",height);
    return 0;
}
//⑥非递归方式求树宽(树宽:节点个数最多的那一层的节点个数)
int GetWidthByLevel(BiTree T){
    if(T == NULL)
        return 0;
    if(T -> lchild == NULL && T -> rchild ==NULL)
        return 1;
    BiTree Queue[MaxSize];//Queue用来存放树的地址
    BiTree p = T;
    int front = -1, rear = -1;
    int last = 0,width = 0;//last用来标记每次上一层rear在该层末尾的值,刚开始有一个元素,rear从-1变成0。
    Queue[++rear] = p;//入队
    while(front != rear){
        p = Queue[++front];
        if(p -> lchild != NULL)
            Queue[++rear] = p -> lchild;
        if(p -> rchild != NULL)
            Queue[++rear] = p -> rchild;
        //判断
        if(front == last){//当front = 上一层rear末尾的值时,说明这一层结束了,下一层所有的元素已经入队了,rear已经指向了front下一层的末尾的值。
            if(rear - front > width){
                width = rear - front;
            }
            last = rear;//让last重新等于新的一层的rear的末尾的值,方便接下来的front和last的比较
        }
    }
    return width;
}
//⑦非递归方式求树高
int GetHeightByLevel(BiTree T){
    if(T == NULL)
        return 0;
    if(T -> lchild == NULL && T -> rchild ==NULL)
        return 1;
    BiTree Queue[MaxSize];//Queue用来存放树的地址
    BiTree p = T;
    int front = -1, rear = -1;
    int last = 0,level = 0;//last用来标记每次上一层rear在该层末尾的值,刚开始有一个元素,rear从-1变成0。
    Queue[++rear] = p;//入队
    while(front != rear){
        p = Queue[++front];
        if(p -> lchild != NULL)
            Queue[++rear] = p -> lchild;
        if(p -> rchild != NULL)
            Queue[++rear] = p -> rchild;
        //判断
        if(front == last){//当front = 上一层rear末尾的值时,说明这一层结束了,
            // 如果有下一层的话,下一层所有的元素已经入队了,rear已经指向了front下一层的末尾的值。
            level++;
            last = rear;//让last重新等于新的一层的rear的末尾的值,方便接下来的front和last的比较
        }
    }
    return level;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值