二叉树

一、简介

二叉树是由 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 # 完美二叉树, 完全二叉树和完满二叉树

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 树和二叉树 1、树的定义 树(Tree)是由一个 或 多个结点 组成的有限集合T,且满足: ①有且仅有一个称为根的...
    利伊奥克儿阅读 1,278评论 0 1
  • 树形结构是一种十分重要的数据结构。二叉树、树与树林都属于树形结构。 树形结构每个结点最多只有一个前驱结点,但可以有...
    cain_huang阅读 1,901评论 0 11
  • 1.树和二叉树的定义 (1) 树的定义 树是n (n≥0) 个结点的有限集。 n=0 时称为空树。在任意一棵非空树...
    yinxmm阅读 2,370评论 0 3
  • 数据结构和算法--二叉树的实现 几种二叉树 1、二叉树 和普通的树相比,二叉树有如下特点: 每个结点最多只有两棵子...
    sunhaiyu阅读 6,341评论 0 14
  • 四、树与二叉树 1. 二叉树的顺序存储结构 二叉树的顺序存储就是用数组存储二叉树。二叉树的每个结点在顺序存储中都有...
    MinoyJet阅读 1,453评论 0 7