数据结构学习笔记之二叉树

1 二叉树结构定义

//二叉树树结点定义
typedef struct BTNode{
    char val;
    struct BTNode *lChild, *rChild;
}BTNode, *BTree;

2 创建二叉树

//使用前序遍历的顺序存储结构创建二叉树
BTree CreatBTreePre(){
    char ch;
    scanf("%c", &ch);
    BTree root;
    if (ch == '#')
        root = NULL;
    else{
        root = (BTree)malloc(sizeof(BTree));
        root->val = ch;
        root->lChild = CreatBTreePre();
        root->rChild = CreatBTreePre();
    }
    return root;
}

3 前序遍历——递归

void PreOrderRe(BTree root){
    if (!root)
        return;
    printf("%c ", root->val);
    PreOrderRe(root->lChild);
    PreOrderRe(root->rChild);
}

4 中序遍历——递归

void InOrderRe(BTree root){
    if (!root)
        return;
    InOrderRe(root->lChild);
    printf("%c ", root->val);
    InOrderRe(root->rChild);
}

5 后序遍历——递归

void PostOrderRe(BTree root){
    if (!root)
        return;
    PostOrderRe(root->lChild);
    PostOrderRe(root->rChild);
    printf("%c ", root->val);
}

6 前序遍历——非递归

//1 沿着根的左孩子依次访问并入栈,直至左孩子为空
//2 栈顶元素出栈:
//  2.1 若其右孩子为空,继续执行
//  2.2 若右孩子不空,将右子树执行 1
void PreOrderNotRe(BTree root){
    Stack *s = (Stack *)malloc(sizeof(Stack));
    InitStack(s);
    BTree ptr = root;   //ptr 为工作指针
    while (ptr || !isEmpty(s)){
        if (ptr){ //只要左子树不为空,一直左走到头,访问结点并入栈
            printf("%c ", ptr->val);
            Push(s, ptr);
            ptr = ptr->lChild;
        } else{
            //左结点走到头,出栈
            Pop(s, &ptr);
            ptr = ptr->rChild;  //工作指针转向该节点的右孩子
        }
    }
}

7 中序遍历——非递归

//1 沿着根的左孩子依次入栈,直至左孩子为空
//2 栈顶元素出栈并访问:
//  2.1 若其右孩子为空,继续执行
//  2.2 若右孩子不空,将右子树执行 1
void InOrderNotRe(BTree root){
    Stack *s = (Stack *)malloc(sizeof(Stack));
    InitStack(s);
    BTree ptr = root;   //ptr 为工作指针
    while (ptr || !isEmpty(s)){
        if (ptr){ //只要左子树不为空,一直想左走
            Push(s, ptr);
            ptr = ptr->lChild;
        } else{
            //左结点走到头,出栈并访问该结点
            Pop(s, &ptr);
            printf("%c ", ptr->val);
            ptr = ptr->rChild;  //工作指针转向该节点的右孩子
        }
    }
}

8 后序遍历——非递归

//1 沿着根的左孩子依次入栈,直到左孩子为空
//2 读取栈顶元素(非出栈):
//  2.1 若右孩子不空且未被访问,将右子树执行 1
//  2.2 否则,栈顶元素出栈并访问
void PostOrderNotRe(BTree root){
    Stack *s = (Stack *)malloc(sizeof(Stack));
    InitStack(s);
    BTree ptr = root;   //工作指针
    BTree pre = NULL;   //记录当前结点右子树是否已经被访问过
    while (ptr || !isEmpty(s)){
        if (ptr){
            Push(s, ptr);
            ptr = ptr->lChild;
        } else{
            getTop(s, &ptr);    //读取栈顶元素
            if (ptr->rChild && ptr->rChild != pre)  //右子树存在且未访问
                ptr = ptr->rChild;  //转向右子树
            else{
                Pop(s, &ptr);   //否则,弹出结点并访问
                printf("%c ", ptr->val);
                pre = ptr;  //记录最近访问的结点
                ptr = NULL; //节点访问完后,重置工作指针
            }
        }
    }
}

9 层序遍历

//1 先将根节点入队,然后出队,访问出队结点
//2 若有左子树,左子树根节点入队
//3 若有右子树,右子树根节点入队
//4 然后出队,访问出队结点,如此循环...直至队列为空
void LevelOrderBTree(BTree root){
    Queue *q = (Queue *)malloc(sizeof(Queue));
    BTree ptr;
    InitQueue(q);
    JoinQueue(q, root);     //根节点优先入队
    while (!IsEmpty(q)){
        LeaveQueue(q, &ptr);    //队列不空时队头出队
        printf("%c ", ptr->val);
        if (ptr->lChild)    //若有左子树,左子树根节点入队
            JoinQueue(q, ptr->lChild);
        if (ptr->rChild)    //若有右子树,右子树根节点入队
            JoinQueue(q, ptr->rChild);
    }
}

10 前序中序还原二叉树

//前序中序重建二叉树
BTree PreAndInCreatBTree(char *preOrder, int preStart, int preEnd, 
                         char *inOrder, int inStart, int inEnd){
    if (preStart > preEnd || inStart > inEnd)
        return NULL;
    
    BTree root = (BTree)malloc(sizeof(BTree));
    root->val = preOrder[preStart];     //当前子树的根节点为前序的第一个值
    root->lChild = NULL;
    root->rChild = NULL;
    
    //定位到前序的根节点在中序中的位置
    int index = inStart;
    for (; index <= inEnd; index++)
        if (inOrder[index] == preOrder[preStart])
            break;
    //递归创建左右子树
    root->lChild = PreAndInCreatBTree(preOrder, preStart+1, preStart + index - inStart, inOrder, inStart, index - 1);
    root->rChild = PreAndInCreatBTree(preOrder, preStart + index - inStart + 1, preEnd, inOrder,index + 1, inEnd);

    return root;
}

11 中序后序还原二叉树

BTree InAndPostCreatBTree(char *inOrder, int inStart, int inEnd, 
                          char *postOrder, int postStart, int postEnd){
    if (inStart > inEnd || postStart > postEnd)
        return NULL;

    BTree root = (BTree)malloc(sizeof(BTree));
    root->val = postOrder[postEnd];
    root->lChild = NULL;
    root->rChild = NULL;

    //定位到后序的根节点在中序中的位置
    int index = inStart;
    for (; index <= inEnd; index++)
        if (inOrder[index] == postOrder[postEnd])
            break;
    root->lChild = InAndPostCreatBTree(inOrder, inStart, index - 1, postOrder, postStart, postStart + index - inStart - 1);
    root->rChild = InAndPostCreatBTree(inOrder, index + 1, inEnd, postOrder, postStart + index - inStart, postEnd - 1);
    return root;
}

12 反向层序遍历

//写出二叉树自下而上、自右至左的层次遍历算法
//先将二叉树按照正常的层序遍历进行遍历
//将每个结点依次加入栈中,然后依次出栈即可实现反向层序遍历
void ReverseLever(BTree root){
    Queue *q = (Queue*)malloc(sizeof(Queue));
    Stack *s = (Stack*)malloc(sizeof(Stack));
    BTree ptr;
    InitStack(s);
    InitQueue(q);
    JoinQueue(q, root);
    while (!IsEmpty(q)){
        LeaveQueue(q, &ptr);
        Push(s, ptr);
        if (ptr->lChild)
            JoinQueue(q, ptr->lChild);
        if (ptr->rChild)
            JoinQueue(q, ptr->rChild);
    }
    while (!isEmpty(s)){
        BTree *dis = (BTree *)malloc(sizeof(BTree));
        getTop(s, dis);
        printf("%c ", (*dis)->val);
        Pop(s, &root);
        free(dis);
    }
}

13 求树高度(深度)

//设计一个非递归算法求二叉树高度
//在层次遍历的同时,记录当前队列的最大长度即可
int GetHight(BTree root){
    if (!root) return 0;
    if (!root->lChild && !root->rChild) return 1;

    int hight = 0;
    Queue *q = (Queue*)malloc(sizeof(Queue));
    InitQueue(q);
    JoinQueue(q, root);

    while (!IsEmpty(q)){
        hight++;
        //当前层的队列宽度
        int index = q->rear - q->front;
        BTree node = NULL;
        for (int i = 0; i < index; ++i) {
            LeaveQueue(q, &node);
            if (node->lChild || node->rChild){
                if (node->lChild)
                    JoinQueue(q, node->lChild);
                if (node->rChild)
                    JoinQueue(q, node->rChild);
            }
        }
    }
    return hight;
}

14 完全二叉树的判定

//判断一个给定的二叉树是否为完全二叉树
//1 使用层序遍历为基础,进行循环判断
//2 当前结点有四种情况:
//  2.1 左右孩子都为空
//  2.2 左孩子不空,右孩子为空
//  2.3 左孩子为空,右孩子不空
//  2.4 左右孩子均不为空
//3 根据以下几种情况判断是否为完全二叉树:
//  3.1 若当前结点出现情况 2.3,直接返回 false,不可能为完全二叉树
//  3.2 若当前结点出现情况 2.4,则继续访问其他节点
//  3.3 若当前结点出现情况 2.2,则从此刻开始,所有的结点都应当是叶节点,
//      即满足情况 2.1,如出现其他情况,说明不是完全二叉树
_Bool IsCompleteBinaryTree(BTree root){
    if (!root) return 1;
    _Bool leaf = 0;     //设置布尔变量来标记是否已经开启了状态 2.2
    Queue *q = (Queue *)malloc(sizeof(Queue));
    InitQueue(q);
    JoinQueue(q, root);

    while (!IsEmpty(q)){
        BTree node = NULL;
        LeaveQueue(q, &node);
        //状态开启的情况下出现了非叶结点 或者 状态未开启但是出现了情况 2.3
        if ((leaf && (node->lChild || node->rChild)) || (!node->lChild && node->rChild))
            return 0;
        if (node->lChild)
            JoinQueue(q, node->lChild);
        if (node->rChild)
            JoinQueue(q, node->rChild);
        //遇见情况 2.1 或 2.2,状态开启
        if ( (node->lChild && !node->rChild) || (!node->rChild && !node->lChild))
            leaf = 1;
    }
    return 1;
}

15 双分支结点数

//计算一棵给定二叉树中双分支结点的个数
//使用层序遍历树节点,若当前结点左右子树均存在,则计数器加一
int DoubleBranchNodeNums(BTree root){
    if (!root) return 0;
    if (!root->rChild && !root->lChild) return 0;

    int count = 0;
    Queue *q = (Queue *)malloc(sizeof(Queue));
    BTree cur = NULL;
    InitQueue(q);
    JoinQueue(q, root);
    while (!IsEmpty(q)){
        LeaveQueue(q, &cur);
        if (cur->lChild && cur->rChild)
            count++;
        if (cur->lChild)
            JoinQueue(q, cur->lChild);
        if (cur->rChild)
            JoinQueue(q, cur->rChild);
    }
    return count;
}

16 左右子树交换

//将二叉树中所有结点的左右子树进行交换
void SwapLeftAndRight(BTree root){
    if (root){
        SwapLeftAndRight(root->lChild);
        SwapLeftAndRight(root->rChild);
        BTree temp = root->lChild;
        root->lChild = root->rChild;
        root->rChild = temp;
        temp = NULL;
    } else 
        return;
}

17 先序第 k 个结点

//求先序遍历序列中第 k 个结点的值
//使用栈进行先序遍历,在遍历的同时设置计数器计数即可
char GetKthInPreOrder(BTree root, int k){
    Stack *s = (Stack *)malloc(sizeof(Stack));
    InitStack(s);
    BTree ptr = root;
    int count = 0;
    while (ptr || !isEmpty(s)){
        if (ptr){
            Push(s, ptr);
            count++;
            if (count == k)
                return ptr->val;
            ptr = ptr->lChild;
        } else{
            Pop(s, &ptr);
            ptr = ptr->rChild;
        }
    }
}

18 删除特定结点子树

//对于树中每一个值为 x 的结点,删去以它为根节点的子树,并释放空间
_Bool DeleteSubtree(BTree root, char x, _Bool flag){
    if (!root)
        return 0;
    else{
        //当前结点为 x,更改标志位为 1,向下传递需要删除的信息
        if (root->val == x)
            flag = 1;
        int leftRes = DeleteSubtree(root->lChild, x, flag);
        int rightRes = DeleteSubtree(root->rChild, x, flag);

        if (flag == 1){ //标志位为 1,说明当前结点处于应被删除的子树中
            if (root->val == x) //若是 x,对上层函数传递信息,以便父节点置空
                return 1;
            root = NULL;
        } else{
            if (leftRes == 1)
                root->lChild = NULL;
            if (rightRes == 1)
                root->rChild = NULL;
        }
    }
    return 0;
}

19 打印特定结点的祖先

//假设二叉树中值为 x 的结点不止一个,打印所有值为 x 的结点的祖先
int XNodeAncestor(BTree root, char x){
    BTree ptr = root;
    if (ptr){
        if (ptr->val == x)
            return 1;
        int left = XNodeAncestor(root->lChild, x);
        int right = XNodeAncestor(root->rChild, x);

        //若一个结点的子树中有要查询的结点,则该结点就是符合要求的祖先结点
        if (left || right){
            printf("%c ", ptr->val);
            return 1;
        } else
            return 0;
    }
    return 0;
}

20 两结点的最近公共祖先

//在一棵树中,找到所给的值为 p 和 q 的结点的最近公共祖先结点
BTree CommonAncestor(BTree root, char p, char q){
    //如果在某一棵子树上找到了 p,则这棵子树无序继续遍历
    //因为即使这棵子树有 q,那么 p 也一定是两个结点的公共祖先
    if (!root || root->val == p || root->val == q)
        return root;
    //按照如上规则,找到左右子树的最近公共祖先
    BTree left = CommonAncestor(root->lChild, p, q);
    BTree right = CommonAncestor(root->rChild, p, q);
    //若左右子树分别找到了 p 和 q,则 root 就是公共祖先
    if (left && right)
        return root;
    //一边找到了一边没找到,则找到的那个就是最近公共祖先
    return left ? left : right;
}

21 满二叉由前序求后序

//假设有一棵满二叉树(结点值均不同),已知其先序序列,求其后序序列
void GetPostOrder(char *pre, int preS, int preE, char *post, int postS, int postE){
    if (preS > preE)
        return;
    int half;
    post[postE] = pre[preS];
    half = (preE - preS) / 2;
    GetPostOrder(pre, preS + 1, preS + half, post, postS, postS + half - 1);
    GetPostOrder(pre, preS + half + 1, preE, post, postS + half, postE - 1);
}

22 叶子结点数

//计算一个二叉树的所有叶子结点数
int GetLeafNodeNums(BTree root){
    int count;
    if (!root)
        count = 0;
    else if (!root->lChild && !root->rChild)
        count = 1;
    else
        count = GetLeafNodeNums(root->lChild) + GetLeafNodeNums(root->rChild);
    return count;
}

23 总结点数

//计算一个二叉树的总结点数
int GetNodeNums(BTree root){
    int count = 0;
    if (!root)
        return 0;
    else
        count = GetNodeNums(root->lChild) + GetNodeNums(root->rChild) + 1;
    return count;
}

24 求树宽度

//求一棵非空二叉树的宽度(即结点数最多的那层中的结点个数)
//使用层序遍历方法进行遍历,在同时统计最长的队列长度即为最大层结点数
int GetWidth(BTree root){
    Queue *q = (Queue *)malloc(sizeof(Queue));
    InitQueue(q);
    BTree ptr = NULL;
    int length = 0;
    JoinQueue(q, root);
    while (!IsEmpty(q)){
        if (q->rear - q->front > length)
            length = q->rear - q->front;
        LeaveQueue(q, &ptr);
        if (ptr->lChild)
            JoinQueue(q, ptr->lChild);
        if (ptr->rChild)
            JoinQueue(q, ptr->rChild);
    }
    return length;
}

25 求树高度

#define max(a, b) ((a)>= (b)) ? (a) : (b)

int GetHeight(BTree root){
    return !root ? 0 : max(GetHeight(root->lChild), GetHeight(root->rChild)) + 1;
}
posted @ 2021-11-01 16:34  悟道九霄  阅读(65)  评论(0编辑  收藏  举报