• C的抽象数据类型:二叉树


    1 二叉树

      二叉树的每个节点都包含一个项用来存储数据,以及两个指向其他子节点的指针用来链接结构;是一种二分查找的树形结构;

      当数据按顺序排列时,使用二叉树从中间的节点开始查找,每次都能排除一半的数据量,效率较高;只是编程较为复杂。

      不过当二叉树子树的排列不平衡时,查找效率挺低的;可能还不如链表实用;

      定义:二叉查找树的每个节点都有两个子树,左子树的节点都在父节点的前面,右子树的节点都在父节点的后面,子树允许为空,但不允许相同;

    2  结构体定义

    //定义二叉树项数最大值为10,如果size==MAXITEMS,表示二叉树满了
    #define MAXITEMS 10  
    typedef struct item
    {
        char petname[20];
        char petkind[20];
    } Item;
    
    /*二叉树结构体:Tree,只要一个根节点就可以确定二叉树的位置了*/
    typedef struct tree
    {
        Node * root; //根节点          
        int size;    //节点数size,通过size来判断二叉树的项数,满,空;          
    } Tree;
    
    /*二叉树节点:Node******_____________________________************
    **********************|*********节点的数据项*********|***********
    **********************|**左子节点指针***右子节点指针**|***********/
    typedef struct node
    {
        Item item;
        struct node * left;    
        struct node * right;  
    } Node;
    
    /*删除节点时,用来存储被删除节点的地址和被删除节点的父节点;*/
    typedef struct pair {
        Node * parent;
        Node * child;
    } Pair;

    3 向二叉树中添加项

    //向二叉树中添加新的节点,新节点的数据为 const Item * pi;
    bool AddItem(const Item * pi, Tree * ptree)
    {
        Node * new_node;
    
        if  (TreeIsFull(ptree))     //1,检查树是否已满;
        {
            fprintf(stderr,"列表存储已满
    ");
            return false;             
        }
        if (SeekItem(pi, ptree).child != NULL)  //2,检查树是否已有相同项;
        {
            fprintf(stderr, "二叉树的项要求不重复
    ");
            return false;             
        }
        new_node = MakeNode(pi);      //3,创建新节点;
        if (new_node == NULL)
        {
            fprintf(stderr, "Couldn't create node
    ");
            return false;             
        }
        ptree->size++;
    
        //4,添加新节点
        if (ptree->root == NULL)      
            ptree->root = new_node;//如果二叉树为空,则直接添加节点;   
        else                          
            AddNode(new_node,ptree->root); //如果二叉树不为空,递归查找添加节点;
        return true;                 
    }
    
    static Node * MakeNode(const Item * pi)
    {
        Node * new_node;
        new_node = (Node *) malloc(sizeof(Node));
        if (new_node != NULL)
        {
            new_node->item = *pi;//将数据放入新节点的item中;
            new_node->left = NULL;//因为是新节点,还没有子节点;
            new_node->right = NULL;
        } 
        return new_node;
    }
    
    //对于添加节点的函数,可以使用递归来添加;
    //先比较节点的数据项,看看当前节点是属于左节点还是右节点;
    //如果左子节点或右子节点刚好为空,就可以直接存入;否则递归调用直到找到空子节点; 
    static void AddNode (Node * new_node, Node * root)
    {   //先new_node->item表示item变量,然后&new_node->item表示传入item变量的地址;
        if (ToLeft(&new_node->item, &root->item))       //表示新节点在父节点的左边
        {
            if (root->left == NULL)        //1.刚好root节点的左子节点为空,
                root->left = new_node;     //于是直接放进去; 
            else  //2.root的两个子节点都不为空,不知道新节点应该放在哪个子节点里;
                AddNode(new_node, root->left);          //递归调用继续比较;
        }
        else if (ToRight(&new_node->item, &root->item)) //表示新节点在父节点的右边
        {
            if (root->right == NULL)
                root->right = new_node;
            else
                AddNode(new_node, root->right);
        }
        else     //新节点与父子节点相等,二叉树的节点数据要求不能重复,返回错误;
        {
            fprintf(stderr,"location error in AddNode()
    ");
            exit(1);
        }
    }
    //对于这种用来比较数据的指针,可以给指针加个const,防止数据误修改;
    static bool ToLeft(const Item * i1, const Item * i2)
    {
        int comp1;
    //字符串比较,strcmp();返回值映射i1 ? i2中间的比较符;
        if ((comp1 = strcmp(i1->petname, i2->petname)) < 0)
            return true;
        else if (comp1 == 0 &&
                    strcmp(i1->petkind, i2->petkind) < 0 )
            return true;
        else
            return false;
    }
    
    static bool ToRight(const Item * i1, const Item * i2)
    {
        int comp1;
        if ((comp1 = strcmp(i1->petname, i2->petname)) > 0)
            return true;
        else if (comp1 == 0 &&
                    strcmp(i1->petkind, i2->petkind) > 0 )
            return true;
        else
            return false;
    }

    4 向二叉树中删除项

    //删除二叉树中,节点数据为const Item * Pi的节点;
    bool DeleteItem(const Item * pi, Tree * ptree)
    {
        Pair look;//child存储待删除节点的地址,parent存储待删除节点的父节点;
        
        look = SeekItem(pi, ptree);//1,查找该节点的地址;
        if (look.child == NULL)  //找不到该节点;节点都没有,自然不用删除;
            return false;
     
        if (look.parent == NULL) //如果该节点是二叉树的根节点;
            DeleteNode(&ptree->root);//删除根节点;
        else if (look.parent->left == look.child)  //如果该节点是根节点的左子节点
            DeleteNode(&look.parent->left);//删除根节点的左子节点;
        else
            DeleteNode(&look.parent->right);//删除根节点的右子节点;
        ptree->size--;      //二叉树节点统计减1
    
        return true;
    }
    
    /*查找一下item项在二叉树中的位置,
    **返回的look.parent为查找项的父节点;
    **返回的look.child为null或item项,是item在二叉树中的位置;*/
    static Pair SeekItem(const Item * pi, const Tree * ptree)
    {
        Pair look;
        look.parent = NULL;
        look.child = ptree->root;
        if (look.child == NULL)
            return look;        
     
        while (look.child != NULL)
        {
            if (ToLeft(pi, &(look.child->item)))
            {
                look.parent = look.child;
                look.child = look.child->left;
            }
            else if (ToRight(pi, &(look.child->item)))
            {
                look.parent = look.child;
                look.child = look.child->right;
            }
            else       /* must be same if not to left or right    */
                break; /* look.child is address of node with item */
        }
     
        return look;  
        //数据所在根节点为look.parent;look.child为NULL,为数据应该插入的位置节点;
    }
    
    /*传入参数是待删除节点在父节点中的位置,为2级指针来着;
    **因为Node *指针本身需要修改,如果只传入Node * ptr,那么修改之后的指针还要返回给父节点;
    **为了方便,我们把被修改指针的地址也传进来,然后把修改后的指针放到该地址里;*/
    static void DeleteNode(Node **ptr)
    {
        Node * temp;
        puts((*ptr)->item.petname);//不知道为什么要打印字符串,感觉然并软;
        if ( (*ptr)->left == NULL)//如果待删除节点的左子节点为空;
        {
            temp = *ptr;
            *ptr = (*ptr)->right;//那只要把待删除节点的右子节点放到待删除节点的父子节点上;
            free(temp);//于是可以删除节点了,节点剩下的二叉树也接到父节点上了;
        }
        else if ( (*ptr)->right == NULL)
        {
            temp = *ptr;
            *ptr = (*ptr)->left;
            free(temp);
        }
        else//如果待删除节点的两个节点都有子树;
        {
        //先找到左子树最右边的NULL;
            for (temp = (*ptr)->left; temp->right != NULL;temp = temp->right)
                continue;
            //然后把右子树连接上NULL;此时的左子树包含了左子树和右子树;
            temp->right = (*ptr)->right;
            temp = *ptr;
            *ptr =(*ptr)->left;
            free(temp);
        }
    }
    
    static void DeleteAllNodes(Node * root)
    {
        Node * pright;
    
        if (root != NULL)
        {
            pright = root->right;
            DeleteAllNodes(root->left);
            free(root);
            DeleteAllNodes(pright);
        }
    }

    5 一些简单的使用函数

    /*初始化新的二叉树结构体;*/
    void InitializeTree(Tree * ptree)
    {
        ptree->root = NULL;
        ptree->size = 0;
    }
    /*判断二叉树的空,满,项数*/
    bool TreeIsEmpty(const Tree * ptree)
    {
        if (ptree->root == NULL)
            return true;
        else
            return false;
    }
    bool TreeIsFull(const Tree * ptree)
    {
        if (ptree->size == MAXITEMS)
            return true;
        else
            return false;
    }
    int TreeItemCount(const Tree * ptree)
    {
        return ptree->size;
    }
    
    //删除树的所有节点
    void DeleteAll(Tree * ptree)
    {
        if (ptree != NULL)
            DeleteAllNodes(ptree->root);
        ptree->root = NULL;
        ptree->size = 0;
    }
    
    //查看树中是否已有当前项
    bool InTree(const Item * pi, const Tree * ptree)
    {
        return (SeekItem(pi, ptree).child == NULL) ? false : true;
    }
    
    //递归遍历树的节点
    void Traverse (const Tree * ptree, void (* pfun)(Item item))
    {
        if (ptree != NULL)
            InOrder(ptree->root, pfun);
    }
    /* 递归遍历节点,通过定义的函数来处理节点数据; */
    static void InOrder(const Node * root, void (* pfun)(Item item))
    {
        if (root != NULL)
        {
            InOrder(root->left, pfun);
            (*pfun)(root->item);
            InOrder(root->right, pfun);
        }
    }
  • 相关阅读:
    Jmeter性能监测及安装插件(推荐)
    测试用例使用传统excel还是思维导图(Xmind、MindManager等)?
    测试用例设计
    一个资深测试员的感悟
    log4j教程 10、PatternLayout
    log4j教程 9、HTMLLayout
    log4j教程 8、日志格式化
    log4j教程 7、日志记录级别
    log4j教程 6、Logger方法
    log4j教程 5、示例程序
  • 原文地址:https://www.cnblogs.com/caesura-k/p/13055220.html
Copyright © 2020-2023  润新知