二叉树

作者: 和风细羽 | 来源:发表于2018-11-09 11:59 被阅读0次

    一、简介

    二叉树是由 n(n>=0)个结点组成的有序集合,集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。

    二叉树有序是为了高效搜索,如果无序,在搜索的时候就只能遍历了 。并且无序的话,就只能手工创建树,不然程序无法获知结点的创建规则

    二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别:
    ①、树中结点的最大度数没有限制,而二叉树结点的最大度数为 2;
    ②、树的结点无左、右之分,而二叉树的结点有左、右之分。

    1、二叉树性质
    ①、在非空二叉树中,第 i 层的结点总数不超过 2i - 1,i >= 1;

    ②、深度为 h 的二叉树最多有 2h - 1 个结点(h >= 1),最少有 h 个结点;

    ③、对于任意一棵二叉树,如果其叶结点数为 N0,而度数为 2 的结点总数为 N2,则 N0 = N2 + 1;

      论证:每次添加一个新结点时,要么 N0、N2 不变,要么 N0、N2 同时 + 1.

    ④、具有 n 个结点的完全二叉树的深度为 [log2n] + 1。(注:[ ]表示向下取整)

    ⑤、有 N 个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:

      若 I 为结点编号则如果 I > 1,则其父结点的编号为 I/2;

      如果 2 * I <= N,则其左孩子的编号为 2 * I;若 2 * I > N,则无左孩子;

      如果 2 * I + 1 <= N,则其右孩子的结点编号为 2 * I + 1;若2*I+1>N,则无右孩子。

    ⑥、给定 N 个节点,能构成 h(N) 种不同的二叉树。

      h(N)为卡特兰数的第 N 项。h(n) = C(2*n,n)/(n+1)。

    ⑦、设有 i 个枝点,I 为所有枝点的道路长度总和,J 为叶的道路长度总和 J = I + 2i。

    二、存储结构

    1、孩子兄弟表示法
    ①、每个结点都有一个指向其第一个孩子的指针
    ②、每个结点都有一个指向其右侧兄弟的指针

    特性:
    ①、能够表示任意的树形结构
    ②、每个结点包含一个数据成员和两个指针成员
    ③、孩子结点指针和兄弟结点指针构成树杈

    typedef struct Node {
    
        int data;
        struct Node * child;
        struct Node * brother;
    
    } Node, Tree;
    

    2、孩子表示法

    每个结点都有一个指向左孩子的指针和一个指向右孩子的指针

    typedef struct Node {
        
        int data;
        
        struct Node * lChild;
        struct Node * rChild;
        
    } Node, Tree;
    
    孩子表示法

    三、满二叉树

    如果二叉树中所有分支结点的度都为 2,并且叶子结点都在统一层次上,则二叉树为满二叉树。

    满二叉树

    四、完全二叉树

    完全二叉树是由满二叉树而引出来的。

    对于深度为 K 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 1 至 n 的结点一一对应时称之为完全二叉树。

    简而言之:完全二叉树从根结点到倒数第二层是满二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。

    特性:
    ①、同样结点数的二叉树,完全二叉树的高度最小;
    ②、完全二叉树的叶子结点仅出现在最下边两层,并且最底层的叶子结点一定出现在左边,倒数第二层的叶子结点一定出现在右边。
    ③、完全二叉树中度为 1 的结点只有左孩子。

    完全二叉树

    五、层次遍历

    手工创建的无序二叉树各个结点的内容没有指定的顺序,可以引入一个队列,辅助逐层遍历二叉树。

    二叉树遍历
    typedef struct TreeNode {
        
        int data;  //数据
        struct TreeNode * lchild;  // 左孩子指针
        struct TreeNode * rchild;  // 右孩子指针
    
    } TreeNode, *Tree;
    
    typedef struct QueueNode {
        
        TreeNode * treeNode;  // 数据
        struct QueueNode * next;   // 指向下一个结点的指针
        
    } QueueNode;
    
    /*  队列结构  */
    typedef struct Queue {
        
        struct QueueNode * front;  // 队头
        struct QueueNode * rear;   // 队尾
        
    } *Queue;
    
    /// 初始化队列
    Queue initQueue()
    {
        Queue queue = (Queue)malloc(sizeof(queue));
        
        if (queue == NULL)
            return NULL;
        
        queue->front = NULL;
        queue->rear = NULL;
        
        return queue;
    }
    
    /// 空队列
    bool isEmptyQueue(Queue queue)
    {
        return (queue == NULL || queue->front == NULL || queue->front->next == NULL || queue->front == queue->rear);
    }
    
    /// 分配一个队列结点
    QueueNode * initQueueNode(TreeNode * treeNode)
    {
        QueueNode * queueNode = (QueueNode *)malloc(sizeof(QueueNode));
        
        if (queueNode == NULL)
            return NULL;
        
        memset(queueNode, 0, sizeof(struct QueueNode));
        queueNode->treeNode = treeNode;
        queueNode->next = NULL;
        
        return queueNode;
    }
    
    /// 入队
    void insertQueueNode(Queue queue, TreeNode * treeNode)
    {
        if (queue == NULL)
            queue = initQueue();
        
        QueueNode * queueNode = initQueueNode(treeNode);
    
        // 空队列
        if (isEmptyQueue(queue)) {
            queue->front = initQueueNode(NULL);
            queue->front->next = queueNode;
            queue->rear = queueNode;
        }
        // 非空队列
        else {
            queueNode->next = queue->rear->next;
            queue->rear->next = queueNode;
            queue->rear = queueNode;
        }
    }
    
    /// 出队
    QueueNode * removeNode(Queue queue)
    {
        // 空队列
        if (isEmptyQueue(queue))
            return NULL;
        
        QueueNode * queueNode = queue->front->next;
        
        // 如果已经是最后一个值
        if (queueNode == queue->rear) {
            queue->front = NULL;
            queue->rear = NULL;
        }
        else {
            queue->front->next = queueNode->next;
        }
        
        return queueNode;
    }
    
    ///  分配一个树结点
    TreeNode * initNode(int data)
    {
        TreeNode * node = (TreeNode *)malloc(sizeof(TreeNode));
        
        if(node == NULL)
            return NULL;
        
    //    memset(node, 0, sizeof(struct TreeNode));
        node->data = data;
        node->lchild = NULL;
        node->rchild = NULL;
        
        return node;
    }
    
    /**
      *   向二叉查找树中添加一个节点,使得新的二叉树还是二叉查找树。
      *  (非递归方法实现)
      */
    void insertNode(Tree tree, int data)
    {
        // 空树
        if (tree == NULL) {
            tree = initNode(data);
        }
        else {
            TreeNode * curNode = tree; // 当前位置结点
            TreeNode * parent = NULL;  // 保存父结点
            
            bool isLeft = false;
            
            while(curNode != NULL) {
                
                parent = curNode;
                
                isLeft = false;
    
                // 比当前结点小,进入左子树
                if(data < curNode->data) {
                    
                    isLeft = true;
                    
                    curNode = curNode->lchild;
                }
                // 比当前结点大,进入右子树
                else if(data > curNode->data) {
                    
                    curNode = curNode->rchild;
                }
            }
            
            TreeNode * node = initNode(data);
            
            if(isLeft) {
                parent->lchild = node;
            }
            else {
                parent->rchild = node;
            }
        }
    }
    
    /// 逐层遍历树
    void printTree(Tree tree)
    {
        if (tree == NULL)
            return;
        
        // 用队列来辅助遍历
        Queue queue = initQueue();
        insertQueueNode(queue, tree);
        
        // 非空队列
        while (!isEmptyQueue(queue)) {
            
            // 打印队头结点内容
            printf("%d\t", queue->front->next->treeNode->data);
            
            // 左结点入队
            if (queue->front->next->treeNode->lchild != NULL)
                insertQueueNode(queue, queue->front->next->treeNode->lchild);
            
            // 右结点入队
            if (queue->front->next->treeNode->rchild != NULL)
                insertQueueNode(queue, queue->front->next->treeNode->rchild);
            
            // 出队
            removeNode(queue);
        }
    }
    
    /// 插入法创建二叉树
    void createTree(Tree tree, int value[], int len)
    {
        for(int i = 0; i < len; i++) {
            insertNode(tree, value[i]);
        }
    }
    
    /// 调用
    {
        int Array[] = { 2, 3, 4, 5, 6, 7, 8, 9 };
        
        Tree tree = initNode(1);
        createTree(tree, Array, 8);
        printTree(tree);
    }
    
    1   2   3   4   5   6   7   8   9
    

    六、按序遍历

    1、先序遍历
      ①、访问根结点;
      ②、先序遍历左子树;
      ③、先序遍历右子树。

    1    2   4   8   9    5   10    3   6    7 
    

    2、中序遍历
      ①、中序遍历左子树;
      ②、访问根结点;
      ③、中序遍历右子树。

    8   4   9    2    10   5    1   6    3    7
    

    3、后序遍历
      ①、后序遍历左子树;
      ③、后序遍历右子树。
      ②、访问根结点;

    8   9   4   10   5    2    6    7    3    1
    

    七、二叉树叠加

    两棵二叉树重叠位置处的数据元素相加,返回一棵在堆空间创建的新的二叉树。

    二叉树叠加

    八、二叉树线索化

    线索化二叉树是将非线性的二叉树转换为线性的双向链表的过程。

    二叉树的线索化能够反映某种二叉树的遍历次序(结点的先后访问次序)。

    线索化二叉树的过程 二叉树线索化的实现

    通过某种遍历方式遍历二叉树,根据遍历次序将二叉树结点依次存储到辅助队列中,最后将辅助队列中保存的结点依次出队并连接,成为双向链表。

    中序遍历:连接时,原二叉树结点的 lChild 指针作为双向链表结点的 pre 指针,指向结点的前驱;原二叉树结点的 rChild 结点作为双向链表结点的 next 指针,指向结点的后继。

    结点连接

    九、学习文章

    天山老妖S # 数据结构(十四)——二叉树
    百度百科
    veli # 完美二叉树, 完全二叉树和完满二叉树

    相关文章

      网友评论

          本文标题:二叉树

          本文链接:https://www.haomeiwen.com/subject/ltggxqtx.html