二叉树的存储

1. 二叉树的性质

  • 性质1二叉树的每个结点最多有两个子结点,分别为左孩子、右孩子,以他们为根的子树称为左子树、右子树。
    • 性质2二叉树的第i层最多有2^i-1^个结点。
1.1满二叉树

定义:在二叉树中, 每一层的结点数都是满的 (见二叉树的性质2),一个n层的满二叉树,结点数量一共有2^n^-1个。
在这里插入图片描述

1.2完全二叉树

定义:满二叉树中 只有最后一层有缺失的结点,并且 缺失的编号都在最后
性质:以一颗结点数量为k的完全二叉树,设0号点为根节点。

  • 性质1:i >1的结点,其父结点是i/2
  • 性质2:如果结点i有孩子,那么它的左孩子是2i,右孩子是2i+1

2.二叉树的存储结构

二叉树一般用指针来实现,并指向左、右子结点。

struct TreeNode {
    char value;            //结点值
    TreeNode *l, *r;        //指向左、右两个子结点
};

完全二叉树可以用数组实现,会更加方便。

二叉树的遍历

1.深度优先遍历

blog.csdnimg.cn/20200507194553816.png)
根节点访问顺序分类:
在这里插入图片描述

tip:根结点已粗体

  • 先序遍历 (根节点——>左儿子——>右儿子):
  • A*-BDE-CFG
  • 中序遍历 (左儿子——>根节点——>右儿子):
    DBE-A-FCG
  • 后序遍 (左儿子——>右儿子——>根节点
    DEB-FGC-A

在进行具体遍历的情况下,应在最后一层结点后面加上空的左右结点NULL,NULL是一条路走完的标志,递归终止的条件。在这里插入图片描述

1.1先序遍历

1.1.1递归写法:

在使用递归方法时,

void PreOrder(const TreeNode *root) {
    if (root == NULL) {
        printf("# ");                 //若结点为空
        return;
    }
    printf("%c ", root->value);        //输出根节点的值
    PreOrder(root->l);             //前序访问左子树
    PreOrder(root->r);            //前序访问右子树
}

1.1.2非递归写法
前序遍历中,栈中元素是自己和自己的左孩子都访问过了,而右孩子还没有访问到的节点。
往左边走,一边输出一边压栈,直到遇NULL,开始弹出栈顶。

void PreOrder_(TreeNode *root) {
    stack<TreeNode *> s;
    TreeNode *cur, *top;
    cur = root;       //用cur指针指向当前访问的结点
    while (cur != NULL || !s.empty()) {//返回上一个根节点 
        while (cur != NULL) {//当前节点非空则输出数值value,压入栈中。
            printf("%c ", cur->value);
            s.push(cur);
            cur = cur->l;//继续指向当前结点的左孩子 
        }
        //直至左孩子为空,从栈中拿出栈顶结点top。
        top = s.top();
        s.pop();
        //上面已经确定了根节点,便可直接访问到右孩子。 
        cur = top->r;
    }
}
1.2中序遍历

1.2.1递归写法:

void InOrder(const TreeNode *root) {
    if (root == NULL) {             //判断节点是否为空
        printf("# ");
        return;
    }
    InOrder(root->l);           //中序遍历左子树
    printf("%c ", root->value);     //访问节点值
    InOrder(root->r);          //中序遍历右子树
}

1.2.2非递归写法:
而中序访问时栈中保存的元素是节点自身和它的右子树都没有被访问到的节点地址。
沿着最左边往下访问,路过的节点全部压栈,直到遇到空节点。

1.3后序遍历

1.3.1递归写法:

void PostOrder(TreeNode *root)  {
    if (root == NULL)  {
        printf("# ");
        return;
    }
    PostOrder(root->l);            //后序遍历左子树
    PostOrder(root->r);            //后序遍历右子树
    printf("%c ", root->value);        //访问节点值
}

1.3.2非递归写法:
栈中保存的元素是它的右子树和自身都没有被遍历到的节点。
与中序遍历不同的是先访问右子树,回来的时候再输出根节点的值。
多一个last指针指向上一次访问到的节点,用来确认是从根节点的左子树返回的还是从右子树返回的。

void PostOrder_(TreeNode *root) {
    stack<TreeNode *> s;
    TreeNode *cur, *top, *last = NULL;
    cur = root;
    while (cur != NULL || !s.empty()) {
        while (cur != NULL) {
            s.push(cur);
            cur = cur->l;
        }

        top = s.top();

        if (top->r == NULL || top->r == last) {
            s.pop();
            printf("%c ", top->value);
            last = top;
        }
        else {
            cur = top->r;
        }
   }
}