深入浅出--二叉树

来源:互联网 发布:盲僧皮肤龙的传人淘宝 编辑:程序博客网 时间:2024/05/26 08:42

二叉树

(题外话:)在数据结构和算法中,数据结构也就是ADT是组织数据的方式,而算法就是解决问题的流程问题;通常我们说数据结构和算法的优化就是两个方面,即空间和时间,尽可能的使空间和时间最小化。基于以上两个原则,才有了不断在原来的经典的算法之上继续改进和创新。

本主今天讲解一下二叉树,二叉树的应用很广泛,尤其是在后面讲解到查找的算法,二叉树是一个效率很高的数据结构,并且在此基础上延伸到B数,B+树,B*树,红黑树等。

二叉树定义

首先说一下什么是树? 树作为一个常用的数据结构,我们可以暂时理解为数据之间的一对多的关系如图所示。
这里写图片描述
二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。
可以把二叉树的定义简单得理解为:如存在根,每个节点的度不大于2,每个结点的孩子结点次序不能任意颠倒(递归定义)。
这里写图片描述

特殊二叉树

1.斜树

所有结点都只有左子树的二叉树叫左斜树,所有结点都只有右子树的二叉树叫右斜树。斜树的每一层都只有一个结点,结点的个数与斜树的深度相同。

2 满二叉树

在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称为满二叉树。(上图中所示的二叉树,就是一棵满二叉树)

3 完全二叉树

对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中的编号为i的结点在二叉树中的位置完全相同,则这棵二叉树称为完全二叉树。

二叉树性质

性质1:在二叉树中至多有2^(n-1)个结点(n为树的深度)。可用数学归纳法证明。

性质2:深度为K的二叉树,至多有(2^n ) -1 个结点。
以上两个性质常会在查找树等问题中用到,如求B树的最小深度。

性质3:对任意一个二叉树,如终端结点n0,而度数为2的结点为n2,则有n0=n2+1。
证明:
设二叉树的结点总数为n,度为0的结点即终端结点为n0,度为1的结点为n1,度为2的结点为n2,则有 n = n0 + n1 + n2,从另一方面讲,度为1的结点有一个子结点,度为2的结点有有两个子结点,度为0的结点有0个子结点,则n = 0 * n0 + 1 * n1 + 2* n2 +1 ,这里为什么加1,加1 是由于加上根结点,由上面两个公式可得 n0 = n2+1。

性质4:具有n个结点的完全二叉树的深度为floor(log2n) + 1 。

性质5:如果对一棵有n个结点的完全二叉树(其深度为floor(log2n) + 1)的结点按层序编号,则对任一结点i(1≤i≤n)有:

(1) 如果i = 1,则结点i是二叉树的根,无双亲;如果i > 1,则其双亲PARENT(i)是结点 floor((i)/2)。

(2)如果2i > n,则结点i无左孩子;否则其左孩子LCHILD(i)是结点2i。

(3)如果2i + 1 > n,则结点i无右孩子;否则其右孩子RCHILD(i)是结点2i + 1。
由此性质可知,如果结点的编号为i(假设其左子树、右子树和父结点都存在),则其左子树的编号为2i,右子树的编号为2i +1, 其父节点为i/2。

二叉树逻辑存储结构

二叉树的逻辑结构如图所示,是客观世界的真实反映,反映了在客观世界中数据之间组织的方式。如图所示。
这里写图片描述

二叉树的物理存储结构

二叉树的物理存储结构即为在二叉树在计算机世界的反映,一般是顺序存储和链式存储。
顺序存储:如图所示
这里写图片描述
由此可以看出,当二叉树为左斜树,右斜树,会有很多的空间浪费。
链式存储:如图所示
这里写图片描述
由此可以看出,链式存储虽然能够节约空间,但是在查找效率上回损失时间。因此,有二叉链表拓展到三叉链表,加个指向父节点的指针。提高查找效率,由此可以看出,数据结构和算法总是在空间和时间上寻求平衡,根据具体情况来设计数据结构和算法。

二叉树的操作(c语言实现)

#include "stdio.h"#include "stdlib.h"#include "string.h"typedef char ElemType;typedef int status;  //状态码#define ERROR 0#define TRUE 1/*定义二叉树结构*/typedef struct Node{    ElemType data;    struct Node *lchild, *rchild;  /*左右孩子*/}BTNode, *BTree;BTree pre;/*前序插入*/void PreOrderInsert(BTree *BT){    char temp;    scanf("%c", &temp);    if(temp =='.')(*BT) = NULL;    else {        (*BT) = (BTNode *) malloc(sizeof(BTNode));        (*BT)->data = temp;        PreOrderInsert(&(*BT)->lchild);        PreOrderInsert(&(*BT)->rchild);    }}/*前序遍历*/void preOrder(BTree T){    if(T!=NULL){        printf("%c",T->data);        preOrder(T->lchild);        preOrder(T->rchild);    }}/*中序遍历*/void InOrder(BTree T){    if(T!=NULL){        preOrder(T->lchild);        printf("%c",T->data);        preOrder(T->rchild);    }}/*后序遍历*/void LastOrder(BTree T){    if(T!=NULL){        preOrder(T->lchild);        preOrder(T->rchild);        printf("%c",T->data);    }}/*打印树形结构*/void PrintTree(BTree BT, int nLayer){    int i = 0;    if(BT == NULL)return ;    PrintTree(BT->rchild, nLayer +1);    for(i = 0;i<nLayer;i++){        printf(" ");    }    printf("%c\n",BT->data);    PrintTree(BT->lchild,nLayer +1);}/*树的深度*/int Depth(BTree BT){    int left = 0, right = 0;    if(BT != NULL){   //自顶向下方法,也可以用从下向顶        left = Depth(BT->lchild) +1;        right = Depth(BT->rchild) +1;        return left > right ? left : right;    }    else return 0;}void main(){    BTree T = NULL;    printf("请输入前序顺序空结点以.形式代表:\n");    PreOrderInsert(&T);    printf("根节点的结构\n");    PrintTree(T,1);    printf("树的深度为:%d\n",Depth(T));}
0 0