数据结构-树

今天介绍的是数据结构中的树,我们从树的概念,树的形状等各个方面介绍一下树:
树的概念:
树(tree)是包含n(n>0)个结点的有穷集,其中:
(1)每个元素称为结点(node);
(2)有一个特定的结点被称为根结点或树根(root)。
(3)除根结点之外的其余数据元素被分为m(m≥0)个互不相交的集合T1,T2,……Tm-1,其中每一个集合Ti(1<=i<=m)本身也是一棵树,被称作原树的子树(subtree)。

节点的度:一个节点含有的子树的个数称为节点的度
叶节点或者终端节点:度为0的点
非终端节点或者分支节点:度不为0的点
双亲节点或者父节点:若一个节点有子节点,那么该节点称为子节点的父节点
同时该子节点也称为父节点的孩子节点或者叫子节点
兄弟节点:具有相同父节点的子节点互称兄弟节点
树的度:一颗树中,最大节点的度称为树的度
节点的层次:从根节点开始,根为第一层,根的子节点为第二层,以此类推
树的高度或者深度:树中节点的最大层次
堂兄弟节点:双亲在同一层的节点
节点的祖先:从根到节点所经分支上的所有节点
子孙:由某节点为根的子树中任一节点都称为该节点的子孙
森林:有多棵互不相交的树组成的集合

二叉树:
二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。如果不考虑连通性,允许图中有多个连通分量,这样的结构叫做森林。
二叉树的形态:
空二叉树,只有根节点的二叉树,只有左子树的二叉树,只有右子树的二叉树,完全二叉树;
(1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
(2)满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。//只有这种情况适用于线性查找

二叉查找树:又称二叉排序树
它是一个动态查找表,所谓动态是指可以增删改查(静态表,只能查找)
性质:
(1)若它的左子树不为空,则左子树上的所有节点的值都小于它的根节点的值;
(2)若它的右子树不为空,则右子树上所有节点的值都大于它的根节点的值;
(3)其他的左右子树也分别为二叉查找树;

二叉查找树适用于以链接的方式存储,保持了链接存储结构在执行插入或者删除时,不用移动元素的优点,二叉排序树的查找性能取决于二叉排序树的形状,但是其形状是不确定的。它的时间复杂度是不稳定的 一般情况下为O(log n),最坏的情况下是O(n),最好的情况是O(1),所以引进了平衡二叉树。

平衡二叉树—平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
平衡因子BF(balance factor):二叉树上的结点的左子树的深度减去右子树的深度的值,平衡二叉树上所有结点的平衡因子只可能是-1,0,1
含有相同节点的二叉查找树可以有不同的形态,而二叉查找树的平均查找长度与树的深度有关,所以需要找出一个查找平均长度最小的一棵,那就是平衡二叉树,具有以下性质:
(1)要么是棵空树,要么其根节点左右子树的深度之差的绝对值不超过1;
(2)其左右子树也都是平衡二叉树;
(3)二叉树节点的平衡因子定义为该节点的左子树的深度减去右子树的深度。则平衡二叉树的所有节点的平衡因子只可能是-1,0,1。
查找和删除的时间复杂度为 O(logn);

为了处理需要不断从硬盘等存储设备中调入或者调出内存页面,为了降低对外存设备的访问次数, 一个结点只能存储一个元素,在元索非常多的时候,就使得要么树的度非常大(结点拥有子树的个数的最大值),要么树的高度非常大,甚全两者都必须足够大才行。这就使得内存存取外存次数非常多,这显然成了时间效率_上的瓶颈。这迫使我们要打破每一个结点只存储一个元素的限制,为此引人了多路查找树的概念。

多路查找树:一个结点的孩子树可以多于俩个,且每个结点处可以存储多个元素
4种特殊的形式:2-3树、2-3-4树、B树(也叫B-树)、B+树

这里只说B树
一种平衡的多叉树,称为B树
它或者是空树,或者是满足下列性质的树:
1、根结点至少有两个子女;
2、每个非根节点所包含的关键字个数 j 满足:「m/2 - 1 ┐<= j <= m - 1;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:「m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。
在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶子结点时,该k-1个关键字正好是k个孩子包含的关键字的值域的分划。
因为叶子结点不包含关键字,所以可以把叶子结点看成在树里实际上并不存在外部结点,指向这些外部结点的指针为空,叶子结点的数目正好等于树中所包含的关键字总个数加1。

B-树中的一个包含n个关键字,n+1个指针的结点的一般形式为: (n,P0,K1,P1,K2,P2,…,Kn,Pn)
其中,Ki为关键字,K1

//AVL树节点信息
template<class T>
class TreeNode
{
    public:
        TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
        T data;//值
        int hgt;//高度
        unsigned int freq;//频率
        TreeNode* lson;//指向左儿子的地址
        TreeNode* rson;//指向右儿子的地址
};
//AVL树类的属性和方法声明
template<class T>
class AVLTree
{
    private:
        TreeNode<T>* root;//根节点
        void insertpri(TreeNode<T>* &node,T x);//插入
        TreeNode<T>* findpri(TreeNode<T>* node,T x);//查找
        void insubtree(TreeNode<T>* node);//中序遍历
        void Deletepri(TreeNode<T>* &node,T x);//删除
        int height(TreeNode<T>* node);//求树的高度
        void SingRotateLeft(TreeNode<T>* &k2);//左左情况下的旋转
        void SingRotateRight(TreeNode<T>* &k2);//右右情况下的旋转
        void DoubleRotateLR(TreeNode<T>* &k3);//左右情况下的旋转
        void DoubleRotateRL(TreeNode<T>* &k3);//右左情况下的旋转
        int Max(int cmpa,int cmpb);//求最大值


    public:
        AVLTree():root(NULL){}
        void insert(T x);//插入接口
        TreeNode<T>* find(T x);//查找接口
        void Delete(T x);//删除接口
        void traversal();//遍历接口

};
//计算节点的高度
template<class T>
int AVLTree<T>::height(TreeNode<T>* node)
{
    if(node!=NULL)
        return node->hgt;
    return -1;
}
//求最大值
template<class T>
int AVLTree<T>::Max(int cmpa,int cmpb)
{
    return cmpa>cmpb?cmpa:cmpb;
}
//左左情况下的旋转
template<class T>
void AVLTree<T>::SingRotateLeft(TreeNode<T>* &k2)
{
    TreeNode<T>* k1;
    k1=k2->lson;
    k2->lson=k1->rson;
    k1->rson=k2;

    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
}
//右右情况下的旋转
template<class T>
void AVLTree<T>::SingRotateRight(TreeNode<T>* &k2)
{
    TreeNode<T>* k1;
    k1=k2->rson;
    k2->rson=k1->lson;
    k1->lson=k2;

    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->rson),k2->hgt)+1;
}
//左右情况的旋转
template<class T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T>* &k3)
{
    SingRotateRight(k3->lson);
    SingRotateLeft(k3);
}
//右左情况的旋转
template<class T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T>* &k3)
{
    SingRotateLeft(k3->rson);
    SingRotateRight(k3);
}
//插入
template<class T>
void AVLTree<T>::insertpri(TreeNode<T>* &node,T x)
{
    if(node==NULL)//如果节点为空,就在此节点处加入x信息
    {
        node=new TreeNode<T>();
        node->data=x;
        return;
    }
    if(node->data>x)//如果x小于节点的值,就继续在节点的左子树中插入x
    {
        insertpri(node->lson,x);
        if(2==height(node->lson)-height(node->rson))
            if(x<node->lson->data)
                SingRotateLeft(node);
            else
                DoubleRotateLR(node);
    }
    else if(node->data<x)//如果x大于节点的值,就继续在节点的右子树中插入x
    {
        insertpri(node->rson,x);
        if(2==height(node->rson)-height(node->lson))//如果高度之差为2的话就失去了平衡,需要旋转
            if(x>node->rson->data)
                SingRotateRight(node);
            else
                DoubleRotateRL(node);
    }
    else ++(node->freq);//如果相等,就把频率加1
    node->hgt=Max(height(node->lson),height(node->rson));
}
//插入接口
template<class T>
void AVLTree<T>::insert(T x)
{
    insertpri(root,x);
}
//查找
template<class T>
TreeNode<T>* AVLTree<T>::findpri(TreeNode<T>* node,T x)
{
    if(node==NULL)//如果节点为空说明没找到,返回NULL
    {
        return NULL;
    }
    if(node->data>x)//如果x小于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->lson,x);
    }
    else if(node->data<x)//如果x大于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->rson,x);
    }
    else return node;//如果相等,就找到了此节点
}
//查找接口
template<class T>
TreeNode<T>* AVLTree<T>::find(T x)
{
    return findpri(root,x);
}
//删除
template<class T>
void AVLTree<T>::Deletepri(TreeNode<T>* &node,T x)
{
    if(node==NULL) return ;//没有找到值是x的节点
    if(x < node->data)
    {
         Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
         if(2==height(node->rson)-height(node->lson))
            if(node->rson->lson!=NULL&&(height(node->rson->lson)>height(node->rson->rson)) )
                DoubleRotateRL(node);
            else
                SingRotateRight(node);
    }

    else if(x > node->data)
    {
         Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
         if(2==height(node->lson)-height(node->rson))
            if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
                DoubleRotateLR(node);
            else
                SingRotateLeft(node);
    }

    else//如果相等,此节点就是要删除的节点
    {
        if(node->lson&&node->rson)//此节点有两个儿子
        {
            TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
            while(temp->lson!=NULL) temp=temp->lson;//找到右子树中值最小的节点
            //把右子树中最小节点的值赋值给本节点
            node->data=temp->data;
            node->freq=temp->freq;
            Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
            if(2==height(node->lson)-height(node->rson))
            {
                if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
                    DoubleRotateLR(node);
                else
                    SingRotateLeft(node);
            }
        }
        else//此节点有1个或0个儿子
        {
            TreeNode<T>* temp=node;
            if(node->lson==NULL)//有右儿子或者没有儿子
            node=node->rson;
            else if(node->rson==NULL)//有左儿子
            node=node->lson;
            delete(temp);
            temp=NULL;
        }
    }
    if(node==NULL) return;
    node->hgt=Max(height(node->lson),height(node->rson))+1;
    return;
}
//删除接口
template<class T>
void AVLTree<T>::Delete(T x)
{
    Deletepri(root,x);
}
//中序遍历函数
template<class T>
void AVLTree<T>::insubtree(TreeNode<T>* node)
{
    if(node==NULL) return;
    insubtree(node->lson);//先遍历左子树
    cout<<node->data<<" ";//输出根节点
    insubtree(node->rson);//再遍历右子树
}
//中序遍历接口
template<class T>
void AVLTree<T>::traversal()
{
    insubtree(root);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: (Tree)是一种非线性的数据结构,它由n(n>=0)个结点构成,其中一个结点为根结点,其余结点可分为m(m>=0)个互不相交的子集T1、T2、……、Tm,其中每一个子集本身又是一棵,并称为根的子的特点是:每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点之外,每个子结点可以分为多个不相交的子。 下面是一个基本的的结构定义: ```c // 的结构体定义 typedef struct TreeNode { int data; // 数据域 struct TreeNode *firstChild;// 第一个子节点 struct TreeNode *nextSibling;// 兄弟节点 }TreeNode, *Tree; ``` 其中,data表示结点的数据域,firstChild指向该结点的第一个子节点,nextSibling指向该结点的下一个兄弟节点。 下面是一个创建的函数: ```c // 创建 Tree createTree(int data) { Tree root = (Tree)malloc(sizeof(TreeNode)); root->data = data; root->firstChild = NULL; root->nextSibling = NULL; return root; } ``` 下面是一个向中添加子节点的函数: ```c // 添加子节点 void addChild(Tree parent, int data) { Tree child = (Tree)malloc(sizeof(TreeNode)); child->data = data; child->firstChild = NULL; child->nextSibling = NULL; if (parent->firstChild == NULL) parent->firstChild = child; else { Tree p = parent->firstChild; while (p->nextSibling != NULL) p = p->nextSibling; p->nextSibling = child; } } ``` 下面是一个先序遍历的函数: ```c // 先序遍历 void preOrderTraversal(Tree root) { if (root != NULL) { printf("%d ", root->data); preOrderTraversal(root->firstChild); preOrderTraversal(root->nextSibling); } } ``` 下面是一个后序遍历的函数: ```c // 后序遍历 void postOrderTraversal(Tree root) { if (root != NULL) { postOrderTraversal(root->firstChild); postOrderTraversal(root->nextSibling); printf("%d ", root->data); } } ``` 下面是一个层次遍历的函数: ```c // 层次遍历 void levelOrderTraversal(Tree root) { Queue q; initQueue(&q); if (root != NULL) { enQueue(&q, root); while (!isQueueEmpty(&q)) { Tree p = deQueue(&q); printf("%d ", p->data); if (p->firstChild != NULL) enQueue(&q, p->firstChild); if (p->nextSibling != NULL) enQueue(&q, p->nextSibling); } } } ``` 其中,Queue是一个队列结构体。 以上就是一个简单的的实现。 ### 回答2: 是一种常见的数据结构,用于存储具有层次关系的数据。它由节点和边组成,通常包含一个根节点和若干子节点。 在C语言中,我们可以使用结构体来定义的节点。一个最基本的节点结构体可能包含两个成员变量:数据和指向子节点的指针。例如: ``` typedef struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; } TreeNode; ``` 上述代码定义了一个名为TreeNode的结构体,它包含一个整型数据成员和两个指向左子节点和右子节点的指针。 为了方便操作,我们可以定义一些基本的函数。其中,创建节点的函数可以使用动态内存分配来分配新节点的内存空间。例如: ``` TreeNode* createNode(int data) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } ``` 我们还可以定义插入节点、删除节点、搜索节点等函数来操作的结构。 除此之外,还可以实现一些遍历的算法,如先序遍历、中序遍历和后序遍历。这些遍历方法可以递归地遍历的节点,并对节点进行指定的操作。 编写的操作函数时,需要考虑到不同的特点,例如二叉搜索要保持左子节点的值小于根节点,右子节点的值大于根节点。 总之,编写一个经典C语言数据结构-需要定义节点的结构体,实现节点的创建、插入、删除和搜索等操作函数,同时可以实现遍历的算法。 ### 回答3: 是一种经典的数据结构,C语言可以很方便地实现的相关操作。首先,我们可以定义一个的节点结构体: ```c typedef struct Node { int data; // 节点存储的数据 struct Node* left; // 左子指针 struct Node* right; // 右子指针 } Node; ``` 接下来,我们可以实现一些的基本操作,例如创建、插入节点、删除节点和遍历。下面是一个简单的示例: ```c // 创建一个节点 Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } // 在中插入一个节点 Node* insertNode(Node* root, int data) { if (root == NULL) { return createNode(data); } else if (data < root->data) { root->left = insertNode(root->left, data); } else if (data > root->data) { root->right = insertNode(root->right, data); } return root; } // 在中删除一个节点 Node* deleteNode(Node* root, int data) { if (root == NULL) { return root; } else if (data < root->data) { root->left = deleteNode(root->left, data); } else if (data > root->data) { root->right = deleteNode(root->right, data); } else { if (root->left == NULL) { Node* temp = root->right; free(root); return temp; } else if (root->right == NULL) { Node* temp = root->left; free(root); return temp; } Node* temp = findMinNode(root->right); root->data = temp->data; root->right = deleteNode(root->right, temp->data); } return root; } // 遍历:前序遍历(中-左-右) void preOrderTraversal(Node* root) { if (root != NULL) { printf("%d ", root->data); preOrderTraversal(root->left); preOrderTraversal(root->right); } } ``` 这只是的基本实现,还有很多其他操作,例如查找节点、判断是否为空、计算的高度等。可以根据具体需求进行扩展。通过以上实现,我们可以使用经典的C语言来构建和操作结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值