数据结构-树

来源:互联网 发布:java软件工程师学费 编辑:程序博客网 时间:2024/06/06 14:08

今天介绍的是数据结构中的树,我们从树的概念,树的形状等各个方面介绍一下树:
树的概念:
树(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);}
0 0
原创粉丝点击