考研数据结构--树和二叉树(1)

树和二叉树

文章目录

定义

形式化定义

在这里插入图片描述

递归定义

在这里插入图片描述

树的(逻辑)表示

树形表示法

在这里插入图片描述

文氏图表示法

在这里插入图片描述

凹入表示法

在这里插入图片描述

括号表示法

在这里插入图片描述

树的基本术语

  • 树结点 (Tree Node) :树中元素的基本单位。包含一个数据元素及若干指向其子树的分支。
  • 结点的度 (Degree) :结点拥有的子树个数。
  • 树的度 (Tree Degree) :树中各结点的度的最大值。
  • 双亲 (Parent)和孩子 (Child) :把一个树结点的直接前驱 (只有一个)称为该结点的双亲;相反的,把一个树结点的所有直接后继 (零到多个)称为该结点的孩子。
  • 兄弟 (Sibling) :同一双亲的孩子之间称为兄弟。
  • 祖先 (Ancenstor):从根到该结点所经分支上的所有结点。
  • 子孙 (Descendant) :以某结点为根的子树中的任一结点。
  • 树的层次 (Level) :从根结点算起,根为第一层,根的孩子为第二层,以此类推。
  • 树的深度 (Depth)或高度:树中各结点层次的最大值。
  • 有序树 (Ordered Tree)和无序树 ((Unordered Tree)) :如果树中结点的各子树可看成从左至右是有次序的 (即不能互换),则称该树为有序树,否则称为无序树。

1. 结点的度与树的度

在这里插入图片描述

2. 分支结点与叶结点

在这里插入图片描述

3. 路径与路径长度

在这里插入图片描述

4. 孩子结点、双亲结点和兄弟结点

在这里插入图片描述

5. 子孙结点和祖先结点

在这里插入图片描述

6. 结点的层次和树的高度

在这里插入图片描述

7. 有序树和无序树

在这里插入图片描述

8. 森林

在这里插入图片描述

在这里插入图片描述

树的性质

性质1:树中的结点数等于所有结点的度数之和加1。

在这里插入图片描述

在这里插入图片描述

度为m的树的其他重要特性

在这里插入图片描述

在这里插入图片描述

小练一下

在这里插入图片描述

性质2:度为m的树中第i层至多有mi-1个结点(i>=1)。

在这里插入图片描述

性质3:高度为h的m叉树至多有(mh-1)/(m-1)个结点。

在这里插入图片描述

等比数列前N项和计算公式:Sn =a1 (1-q^n)/ (1-q)

性质4:具有n个结点的m叉树的最小高度为⌈logm(n(m-1)+1)⌉。

在这里插入图片描述

在这里插入图片描述

树的基本运算

树的运算主要分为三大类:

  • 查找满足某种特定关系的结点,如查找当前结点的双亲结点等。
  • 插入或删除某个结点,如在树的当前结点上插入一个新结点或删除当前结点的第i个孩子结点等。
  • 遍历树中每个结点。

树的遍历

在这里插入图片描述

树的先根遍历

在这里插入图片描述

树的后根遍历

在这里插入图片描述

树的层次遍历

在这里插入图片描述

树的存储结构

1. 双亲存储结构

在这里插入图片描述

在这里插入图片描述

typedef struct {
    int data;
    int parent;
}PTree[MaxSize];

优缺点

双亲存储结构的优点是可以方便地找到任意结点的双亲结点,但是缺点是不容易找到任意结点的孩子结点,也不方便计算树的深度和度数。

2. 孩子链式存储结构

在这里插入图片描述

在这里插入图片描述

优缺点

孩子链式存储结构的优点是可以方便地找到任意结点的所有孩子结点,但是缺点是不容易找到任意结点的双亲结点,也不方便计算树的深度和度数。

定义MaxSize为3,然后定义TSonNode类型,如下:

#define MaxSize 3
typedef struct node{
    int data;
    struct node *sons[MaxSize];
} TSonNode;

然后,创建10个TSonNode类型的结点,并给它们赋值,如下:

//创建10个结点
TSonNode *nodes[10];
for(int i = 0; i < 10; i++){
    //为每个结点分配内存空间
    nodes[i] = (TSonNode *)malloc(sizeof(TSonNode));
    //为每个结点的data赋值
    nodes[i]->data = i;
    //为每个结点的sons数组赋初值NULL
    for(int j = 0; j < MaxSize; j++){
        nodes[i]->sons[j] = NULL;
    }
}

接下来,建立结点之间的关系,即设置sons数组的指针,如下:

//建立结点之间的关系
nodes[0]->sons[0] = nodes[1]; //节点0的第一个孩子是节点1
nodes[0]->sons[1] = nodes[2]; //节点0的第二个孩子是节点2
nodes[1]->sons[0] = nodes[3]; //节点1的第一个孩子是节点3
nodes[1]->sons[1] = nodes[4]; //节点1的第二个孩子是节点4
nodes[2]->sons[0] = nodes[5]; //节点2的第一个孩子是节点5
nodes[2]->sons[1] = nodes[6]; //节点2的第二个孩子是节点6
nodes[3]->sons[0] = nodes[7]; //节点3的第一个孩子是节点7
nodes[4]->sons[0] = nodes[8]; //节点4的第一个孩子是节点8
nodes[6]->sons[0] = nodes[9]; //节点6的第一个孩子是节点9

最后,遍历这棵树,打印出每个结点的数据和孩子结点的数据,如下:

//遍历这棵树,打印出每个结点的数据和孩子结点的数据
for(int i = 0; i < 10; i++){
    printf("node %d: data = %d, sons = ", i, nodes[i]->data);
    for(int j = 0; j < MaxSize; j++){
        if(nodes[i]->sons[j] != NULL){
            printf("%d ", nodes[i]->sons[j]->data);
        }
        else{
            printf("NULL ");
        }
    }
    printf("\n");
}

运行这段代码,可以得到以下输出:

node 0: data = 0, sons = 1 2 NULL 
node 1: data = 1, sons = 3 4 NULL 
node 2: data = 2, sons = 5 6 NULL 
node 3: data = 3, sons = 7 NULL NULL 
node 4: data = 4, sons = 8 NULL NULL 
node 5: data = 5, sons = NULL NULL NULL 
node 6: data = 6, sons = 9 NULL NULL 
node 7: data = 7, sons = NULL NULL NULL 
node 8: data = 8, sons = NULL NULL NULL 
node 9: data = 9, sons = NULL NULL NULL 

3. 孩子兄弟链存储结构

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

优缺点

孩子兄弟链存储结构的优点是可以将任意一棵树转化为一棵二叉树,从而利用二叉树的算法和性质,也可以方便地找到任意结点的孩子结点和兄弟结点,但是缺点是不容易找到任意结点的双亲结点,也不方便计算树的深度和度数。

首先,定义TSBNode类型,如下:

typedef struct tnode{
    int data;
    struct tnode *hp;
    struct tnode *vp;
} TSBNode;

然后,创建9个TSBNode类型的结点,并给它们赋值,如下:

//创建9个结点
TSBNode *nodes[9];
for(int i = 0; i < 9; i++){
    //为每个结点分配内存空间
    nodes[i] = (TSBNode *)malloc(sizeof(TSBNode));
    //为每个结点的data赋值
    nodes[i]->data = i;
    //为每个结点的hp和vp赋初值NULL
    nodes[i]->hp = NULL;
    nodes[i]->vp = NULL;
}

接下来,建立结点之间的关系,即设置hp和vp指针,如下:

//建立结点之间的关系
nodes[0]->vp = nodes[1]; //节点0的第一个孩子是节点1
nodes[1]->hp = nodes[2]; //节点1的水平方向的下一个兄弟是节点2
nodes[1]->vp = nodes[3]; //节点1的第一个孩子是节点3
nodes[2]->hp = nodes[4]; //节点2的水平方向的下一个兄弟是节点4
nodes[2]->vp = nodes[5]; //节点2的第一个孩子是节点5
nodes[3]->hp = nodes[6]; //节点3的水平方向的下一个兄弟是节点6
nodes[4]->vp = nodes[7]; //节点4的第一个孩子是节点7
nodes[6]->hp = nodes[8]; //节点6的水平方向的下一个兄弟是节点8

最后,遍历这棵树形表,打印出每个结点的数据和水平方向和垂直方向的下一个结点的数据,如下:

//遍历这棵树形表,打印出每个结点的数据和水平方向和垂直方向的下一个结点的数据
for(int i = 0; i < 9; i++){
    printf("node %d: data = %d, hp = ", i, nodes[i]->data);
    if(nodes[i]->hp != NULL){
        printf("%d ", nodes[i]->hp->data);
    }
    else{
        printf("NULL ");
    }
    printf(", vp = ");
    if(nodes[i]->vp != NULL){
        printf("%d ", nodes[i]->vp->data);
    }
    else{
        printf("NULL ");
    }
    printf("\n");
}

运行这段代码,可以得到以下输出:

node 0: data = 0, hp = NULL , vp = 1 
node 1: data = 1, hp = 2 , vp = 3 
node 2: data = 2, hp = 4 , vp = 5 
node 3: data = 3, hp = 6 , vp = NULL 
node 4: data = 4, hp = NULL , vp = 7 
node 5: data = 5, hp = NULL , vp = NULL 
node 6: data = 6, hp = 8 , vp = NULL 
node 7: data = 7, hp = NULL , vp = NULL 
node 8: data = 8, hp = NULL , vp = NULL 

二叉树

递归定义

在这里插入图片描述

例子

在这里插入图片描述

二叉树的五种基本形态

在这里插入图片描述

逻辑结构表示法

在这里插入图片描述

二叉树和二次树的区别

在这里插入图片描述

两种特殊的二叉树(满二叉树和完全二叉树)

满二叉树

在这里插入图片描述
在这里插入图片描述

完全二叉树

在这里插入图片描述

完全二叉树的特点

在这里插入图片描述

例题

在这里插入图片描述

二叉树的性质

性质1

在这里插入图片描述

求解一般二叉树结点个数方法归纳

在这里插入图片描述

性质2

性质3

在这里插入图片描述

性质4:(完全二叉树的性质)

在这里插入图片描述

在这里插入图片描述

求解完全二叉树结点个数方法归纳

在这里插入图片描述

森林、树转换为二叉树

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二叉树还原为森林、树

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二叉树的存储结构

二叉树的顺序存储结构

在这里插入图片描述

完全二叉树的顺序存储结构

在这里插入图片描述

一般二叉树的顺序存储结构

在这里插入图片描述

二叉树的顺序存储结构的特点

在这里插入图片描述

在这里插入图片描述

二叉树的链式存储结构

在这里插入图片描述

typedef struct btNode{
    char data;
    struct btNode *lChild, *rChild;
} BTNode, *BTree;

二叉链存储结构示例

在这里插入图片描述

二叉链存储结构的特点

在这里插入图片描述

求二叉链的空指针个数

在这里插入图片描述

二叉树的基本运算

在这里插入图片描述

在这里插入图片描述

创建二叉树

// 创建一个二叉树节点
BTree createNode(char data) {
    BTree node = (BTree)malloc(sizeof(BTNode));
    node->data = data;
    node->lChild = NULL;
    node->rChild = NULL;
    return node;
}

// 创建一个二叉树
BTree createTree() {
    char data;
    scanf("%s", &data); // 输入节点数据
    if (data == '#') { // 如果是'#',表示空节点,返回NULL
        return NULL;
    }
    BTree root = createNode(data); // 创建根节点
    root->lChild = createTree(); // 递归创建左子树
    root->rChild = createTree(); // 递归创建右子树
    return root; // 返回根节点
}

在这里插入图片描述

销毁二叉树

在这里插入图片描述

在这里插入图片描述

查找结点

查找一个二叉树中的某个节点的一种方法是使用递归的方式,从根节点开始,比较节点的数据和要查找的数据,如果相等,就返回该节点,如果不相等,就分别在左子树和右子树中递归查找,如果找到了,就返回找到的节点,如果没有找到,就返回NULL。

// 查找一个二叉树中的某个节点
BTree searchNode(BTree root, char data) {
    if (root == NULL) { // 如果根节点为空,返回NULL
        return NULL;
    }
    if (root->data == data) { // 如果根节点的数据和要查找的数据相等,返回根节点
        return root;
    }
    BTree left = searchNode(root->lChild, data); // 递归在左子树中查找
    if (left != NULL) { // 如果在左子树中找到了,返回找到的节点
        return left;
    }
    BTree right = searchNode(root->rChild, data); // 递归在右子树中查找
    if (right != NULL) { // 如果在右子树中找到了,返回找到的节点
        return right;
    }
    return NULL; // 如果都没有找到,返回NULL
}

在这里插入图片描述

在这里插入图片描述

找孩子结点

查找一个二叉树中的某个节点的孩子节点的一种方法是使用递归的方式,从根节点开始,比较节点的数据和要查找的数据,如果相等,就返回该节点的左孩子和右孩子,如果不相等,就分别在左子树和右子树中递归查找,如果找到了,就返回找到的节点的孩子节点,如果没有找到,就返回NULL。

// 查找一个二叉树中的某个节点的孩子节点
void searchChildren(BTree root, char data, BTree &leftChild, BTree &rightChild) {
    if (root == NULL) { // 如果根节点为空,返回NULL
        leftChild = NULL;
        rightChild = NULL;
        return;
    }
    if (root->data == data) { // 如果根节点的数据和要查找的数据相等,返回根节点的左孩子和右孩子
        leftChild = root->lChild;
        rightChild = root->rChild;
        return;
    }
    searchChildren(root->lChild, data, leftChild, rightChild); // 递归在左子树中查找
    if (leftChild != NULL || rightChild != NULL) { // 如果在左子树中找到了,返回
        return;
    }
    searchChildren(root->rChild, data, leftChild, rightChild); // 递归在右子树中查找
}

求高度

在这里插入图片描述

// 定义一个max方法
int max(int a, int b) {
    if (a > b) { // 如果a大于b,返回a
        return a;
    }
    else { // 否则,返回b
        return b;
    }
}
// 求一个二叉树的高度
int getHeight(BTree root) {
    if (root == NULL) { // 如果根节点为空,返回0
        return 0;
    }
    int leftHeight = getHeight(root->lChild); // 递归求左子树的高度
    int rightHeight = getHeight(root->rChild); // 递归求右子树的高度
    return max(leftHeight, rightHeight) + 1; // 返回较大的高度加1
}

输出二叉树

在这里插入图片描述

// 用二叉树的括号表示来输出二叉树
void printTreeWithBrackets(BTree root) {
    if (root == NULL) { // 如果根节点为空,不打印任何东西
        return;
    }
    printf("%c", root->data); // 打印根节点的数据
    printf("("); // 打印一个左括号
    printTreeWithBrackets(root->lChild); // 递归输出左子树
    printf(")"); // 打印一个右括号
    printf(",");
    printf("("); // 打印一个左括号
    printTreeWithBrackets(root->rChild); // 递归输出右子树
    printf(")"); // 打印一个右括号
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值