Bootstrap

【数据结构】二叉树的建立与遍历

1.二叉树

1.1 二叉树的定义

        首先先来回顾一下什么是二叉树:

        二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。

二叉树的五种可能的形态:

1.2 二叉树相关术语

  • 根节点(root node):位于二叉树顶层的节点,没有父节点。
  • 叶节点(leaf node):没有子节点的节点,其两个指针均指向None 。
  • 边(edge):连接两个节点的线段,即节点引用(指针)。
  • 节点所在的层(level):从顶至底递增,根节点所在层为 1 。
  • 节点的度(degree):节点的子节点的数量。在二叉树中,度的取值范围是 0、1、2 。
  • 二叉树的高度(height):从根节点到最远叶节点所经过的边的数量。
  • 节点的深度(depth):从根节点到该节点所经过的边的数量。
  • 节点的高度(height):从距离该节点最远的叶节点到该节点所经过的边的数量。

1.3 二叉树的性质

性质1:二叉树的第 i 层上至多有2^{i-1} 个结点(i\geq 1)

性质2:深度为 k 二叉树至多有 2^{k}-1个结点(k\geq 1)

性质3:对任意一棵二叉树T,如果其叶子数为n_{0} ,度为2的结点数为n_{2}, 则 n_{0}=n_{2}+1

性质4:具有 n (n\geq 0) 个结点的完全二叉树的深度为 log_{2}(n+1) 或 log_{2}n+1

性质5:如果对一棵具有 n 个结点的完全二叉树的结点进行编号,则对任一结

i  (1\leq i\leq n ) 有:
        

        (1) 若 i=1 ,则该结点是二叉树的根,无双亲; 否则,其双亲结点的编号为 \frac{i}{2} \frac{i-1}{2} ;

        (2)如果 2i>n 则结点 i 没有左孩子, 否则,其左孩子的编号为 2i

        (3)如果 2i+1>n 则结点 i 没有右孩子,否则,其右孩子的编号为 2i+1

        (4)若 i 为奇数, i\neq 1,则其左兄弟为 i-1,

                 若 i 为偶数,  i\neq n, 则其右兄弟为i+1 

2.二叉树的遍历

从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:

① 访问结点本身(N)

② 遍历该结点的左子树(L)

③ 遍历该结点的右子树(R)

在二叉树中,常见的遍历有先序遍历、中序遍历、后序遍历以及层序遍历,他们的主要思想分别是:

前序遍历:根结点 --> 左子树 --> 右子树

// 先序遍历
void PreOrderTraverse(BiTree T) {
    if (T == NULL) {
        return;
    }
    cout << T->data;
    PreOrderTraverse(T->lchild);
    PreOrderTraverse(T->rchild);
}

 中序遍历:左子树 --> 根结点 --> 右子树

// 中序遍历
void InOrderTraverse(BiTree T) {
    if (T == NULL) {
        return;
    }
    InOrderTraverse(T->lchild);
    cout << T->data;
    InOrderTraverse(T->rchild);
}

后序遍历:左子树 --> 右子树 --> 根结点

// 后序遍历
void PostOrderTraverse(BiTree T) {
    if (T == NULL) {
        return;
    }
    PostOrderTraverse(T->lchild);
    PostOrderTraverse(T->rchild);
    cout << T->data;
}

 层次遍历:自上而下,自左至右逐层访问树的结点

// 层序遍历
void LevelOrderTraverse(BiTree T) {
    BiTree queue[100]; // 队列
    int front = 0, rear = 0;
    if (T != NULL) { 
        rear = (rear + 1) % 100;  
        queue[rear] = T; 
    }
    while (front != rear) { 
        int levelSize = (rear - front + 100) % 100; // 当前层的结点个数
        for(int i = 0; i < levelSize; ++i){
            front = (front + 1) % 100;
            BiTree p = queue[front];
            cout << p->data;
            // 将当前结点的左右孩子入队 
            if (p->lchild != NULL) { 
                rear = (rear + 1) % 100;
                queue[rear] = p->lchild;
            }
            if (p->rchild != NULL) { 
                rear = (rear + 1) % 100;
                queue[rear] = p->rchild;
            }
        }
        cout << endl;
    }
}

层序遍历的算法思想

①初始化一个辅助队列

②根节点入队

③若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)

 

// 层序遍历
void LevelOrderTraverse(BiTree T) {
    BiTree queue[100]; // 队列
    int front = 0, rear = 0;
    if (T != NULL) { 
        rear = (rear + 1) % 100;  
        queue[rear] = T; 
    }
    while (front != rear) { 
        int levelSize = (rear - front + 100) % 100; // 当前层的结点个数
        for(int i = 0; i < levelSize; ++i){
            front = (front + 1) % 100;
            BiTree p = queue[front];
            cout << p->data;
            // 将当前结点的左右孩子入队 
            if (p->lchild != NULL) { 
                rear = (rear + 1) % 100;
                queue[rear] = p->lchild;
            }
            if (p->rchild != NULL) { 
                rear = (rear + 1) % 100;
                queue[rear] = p->rchild;
            }
        }
        cout << endl;
    }
}

 

;