二叉树

作者: 和风细羽 | 来源:发表于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 # 完美二叉树, 完全二叉树和完满二叉树

相关文章

  • 数据结构与算法-二叉树02

    二叉树的定义 二叉树的特点 二叉树的五中基本形态 其他二叉树 斜二叉树 满二叉树 完全二叉树图片.png满二叉树一...

  • 二叉树

    二叉树 高度 深度真二叉树 满二叉树 完全二叉树 二叉树遍历前序 中序 后序层序遍历 翻转二叉树 递归法...

  • 二叉树 基础操作

    二叉树的使用 二叉树结构 先序创建二叉树 DFS 先序遍历二叉树 中序遍历二叉树 后序遍历二叉树 BFS 层次遍历...

  • 树与二叉树

    **树 ** 二叉树 满二叉树 完全二叉树 三种遍历方法 树与二叉树的区别 二叉查找树 平衡二叉树 红黑二叉树

  • 二叉树的宽度优先搜索(层次遍历,BFS)

    二叉树结构: 二叉树宽度优先搜索: 按照二叉树的层数依次从左到右访问二叉树的节点;例如:给定一个二叉树: 按照宽度...

  • 剑指 offer:39、平衡二叉树

    39. 平衡二叉树 题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树。 解题思路: 平衡二叉树:Wiki:在...

  • Algorithm小白入门 -- 二叉树

    二叉树二叉树构造二叉树寻找重复子树 1. 二叉树 基本二叉树节点如下: 很多经典算法,比如回溯、动态规划、分治算法...

  • 14-树&二叉树&真二叉树&满二叉树

    一、树 二、二叉树 三、真二叉树 四、满二叉树

  • 二叉树的应用

    完美二叉树(满二叉树) 除了最下一层的节点外,每层节点都有两个子节点的二叉树为满二叉树 完全二叉树 除二叉树最后一...

  • 12.树Tree(2)

    目录:1.二叉树的基本概念2.二叉树的性质3.二叉树的创建4.二叉树的遍历 1.二叉树的基本概念 2.二叉树的性质...

网友评论

      本文标题:二叉树

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