数据结构复习——树与二叉树

本文详细介绍了树的基本概念,如节点度、高度、森林、有序与无序树,重点剖析了二叉树的定义、存储结构(顺序与链式)、遍历方法(先序、中序、后序、层次),涉及线索二叉树、树与二叉树的转换,以及二叉搜索树、平衡二叉树、霍夫曼树和并查集在实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)树的基本概念

定义:

节点的度:节点的子树个数

树的度:节点度的最大的度

高度:就是深度

深度:根节点的深度为1,下面每一层深度加1

分支节点:度不为0的节点

叶节点:度为0的节点

森林:0个或者多个不相交的树

有序树:各子树的顺序有关

无序树:各子树的顺序无关

性质:

树的节点的个数 = 树的所有节点度的个数求和 + 1;

层数为n的树的节点总个数:最多的情况为:满m叉树的情况

以及树的第n层的节点个数 最多的情况为:m的(n-1)次方 

(二)二叉树

1.二叉树的定义及其主要特征

二叉树:每个节点的度最多为2

主要特征:

1.第i层上的节点个数最多为 2的(i-1)次方

2.n层二叉树 节点个数最多为2的i次方-1

3.二叉树的度为2的节点 + 1 = 度为0的节点 

2.二叉树的顺序存储结构

完全二叉树的情况,可以采用数组,根节点下标为1,节点为i的左子树下标为2*i,右子树下标为2*i+1,父节点为 i/2 (向下取整)。

3.二叉树的链式存储结构

typedef strcut BitNode{

int x;

struct BitNode* lchild, * rchild;

}BiTNode,*BitTree;

4.二叉树的遍历

1.先序遍历

void PreOrderTraverse(BitTree root)
{
    visit(root);
    if(root->lchild) PreOrderTraverse(root->lchild);
    if(root->rchild) PreOrderTraverse(root->rchild);
}


void PreOrderTraverse(BitTree root)
{
    stack<BiTNode*> S;
    S.push(root);
    BitNode *p = root;
    while(p || !S.empty()) {
        while(p) {
            visit(p);
            S.push(p);
            p = p->lchild;
        }
        p = S.top();
        S.pop();
        p = p->rchild;
    }
}

2.中序遍历

void InOrderTraverse(BitTree root)
{
    if(root->lchild) InOrderTraverse(root->lchild);
    visit(root);
    if(root->rchild) InOrderTraverse(root->rchild);
}

void InOrderTraverse(BitTree root)
{
    Stack<int> S;
    BitNode* p = root;
    while(p) {
        S.push(p);
        p = p->lchild;
    }
    p = S.top();
    S.pop();
    visit(p);
    p = p->rchild;
}

3.后序遍历

void PostOrderTraverse(BitTree root)
{
    if(root->lchild) PostOrderTraverse(root->lchild);
    if(root->rchild) PostOrderTraverse(root->rchild);
    visit(root);
}

void PostOrderTraverse(BitTree root)
{
    stack<BitTree> S;
    BitNode* p = root;
    while(p || !S.empty())
    {
        while(p) {
            S.push(p);
            p = p->lchild;
        }
        
        p = S.top();
        if(p->rchild&&p->rchild != r)
        {
            p = p->rchild;
        }
        else {
            visit(p);
            S.pop();
            r = p;
            p = null;
        }

    }
    
}

4.层次遍历

void LevelTraverse(BitTree root)
{
    Queue<BitNode*> Q;
    Q.push(root);
    BitNode* temp;
    while(!Q.empty())
    {
        temp = Q.front();
        visit(temp);
        Q.pop();
        if(temp->lchild) Q.push(temp->lchild);
        if(temp->rchild) Q.push(temp->rchild);
    }
}

4.线索二叉树的基本概念和构造

(三)树、森林

1.树的存储结构

1.孩子链表表示法,采用顺序结构+链式结构。

优点:方便找到某一个节点的所有孩子节点

缺点:要找到某一个孩子节点父节点,需要遍历每个节点后跟的链式结构

typedef struct childNode 
{
    int no;
    struct childNode* next;
};

typedef struct Node
{
    int value;
    childNode* firstchild; 
};

typedef struct CTree 
{
    Node[MAX_SIZE] ;
    int n,r;
}CTree;

2.双亲表示法

优点:方便找到父节点

缺点:不方便找到某一个节点的子节点

typedef struct {
    int  value;
    int parent;
}Node;

typedef struct {
    Node nodes[MAX_SIZE];
    int n;
}PTree;

3.孩子兄弟表示法(也称为二叉树表示法)

优点:方便找到孩子节点

缺点:不方便找到某一个节点的父节点,可以加上一个parent节点进行改进

typedef struct CSNode {

int data;

struct CSNode* firstchild,*nextsibling;

}CSNode,*CSTree

2树、森林与二叉树的转换

1.树与二叉树的转换:

采用孩子-兄弟表示法,节点的左lchild表示第一个孩子,rchild实际是该节点的第一个右兄弟。

2.森林与二叉树的转换:

由于每个树转换成二叉树的根节点,是没有rchild的,因此将不同的树的根节点连接到前一个树的根节点的rchild上。将二叉树转换成森林时,切分掉每个根节点的rchild变成树,再用树与二叉树的转换。

3.树和森林的遍历

1.对树的遍历:

先根遍历:先遍历根,在遍历子树

后根遍历:先遍历子树,再遍历根

2.对森林的遍历:

先序遍历森林:对每个树进行先序遍历,等价于对森林转换成的二叉树进行先序遍历

中序遍历森林:对每个数进行后序遍历,等价于对森林转换成的二叉树进行中序遍历

(四)树和二叉树的应用

1.二叉搜索树:

折半查找会画二叉搜索树。一串数值按照大小顺序排好,根节点为中数,根节点的左节点为中数 左边的中数,根节点的右节点为中数 右边的中数,这样下来直观的效果是左子树的上的所有值都小于根节点,右子树上的所有值都大于根节点。若采用中序遍历,得到的是有序的序列。

二叉搜索树的插入:插入的只能是叶子结点,首先根据插入值与原来的值的左右子树值大小进行判断,不断搜索到叶子结点,发现没有匹配到,则接下来根据该值与该叶子节点的大小,将该值插入到该叶子结点的左子树或者右子树上。(需要两个指针,一个指针指向当前遍历的指针,另一个指针为该节点的父节点指针)

二叉搜索树的删除:1.删除叶子结点,有一个指针记录该叶子结点的父节点,直接删除掉父节点的lchild或者rchild。

2.删除度为1的节点,该度为1的节点指针记为p,其父节点记为pa,若p是pa的右孩子,则将p的子树当做pa的右孩子;若p是pa的左孩子,则将p的子树当做pa的左孩子

3.删除度为2的节点,该度为2的节点指针记为r,第一种方法是从r的所有左子树中选择一个值最大的节点(该节点肯定没有右子树),覆盖掉节点r,然后按照“删除度为1或0的节点”的方法删除该节点;第二种方法是从r的所有右子树中选择一个值最小的节点,覆盖掉节点r,然后按照“删除度为1或者0的节点”的方法删除掉该节点。

2.平衡二叉树

平衡二叉树的调整:

万能方法:写出中序遍历,然后按照中序遍历画出二叉搜索树。

一般方法,分为四种情况RR型、LL型、RL型、LR型

3.霍夫曼树和霍夫曼编码

给定每个叶子结点的权重,要求到所有叶子结点的路径长度和最小。

应用场景:霍夫曼编码。

4.并查集

主要是在图中的应用,相互连通的节点共用一个节点作为根节点,这些节点都看做该根节点的孩子。

应用场景:

1.使用邻接矩阵看无向连通图的连接性。合并两个节点,不断为其root重新赋值,若最后所有的节点的root为一个值,那么说明连通;否则有几个root就有几个连通分量。

2.最小生成树的克鲁斯卡尔算法:为防止有环产生,不在root相同的两个节点之间加边(不合并root相同的两个节点)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值