【c语言】树

目录

树的简介

树的实现

注意事项


树的简介

在C语言中,树是一种数据结构,它由节点和边组成,用于表示层次关系或树形结构。树的节点通常包含一个值和指向其子节点的指针。根节点是树的顶部,它没有父节点,而叶节点是没有子节点的节点。

C语言中树的常见应用包括二叉搜索树、堆、表达式树、前缀树等。在实现树的过程中,需要使用指针和递归等C语言特性。

树的实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct linking_node{
    struct linking_node* next ; 
    struct tree_node* node ;
}linking_node ;

typedef struct tree_node{
    int element ;
    struct tree_node* parent ; 
    struct linking_node* head ;
    struct linking_node* tail ;
    int node_count ;
}tree_node ;


void traverse( tree_node* target_node )
{
    printf( "%d\t" , target_node->element ) ;
    linking_node* temp = target_node->head ;
    
    while(temp)
    {
        traverse(temp->node) ;
        temp = temp->next ;
    }
}


tree_node* increase_node(tree_node* target_node , int data ) 
{
    linking_node* new_link_node = (linking_node* )malloc( sizeof(linking_node) ) ;
    tree_node* new_tree_node = (tree_node* )malloc( sizeof(tree_node) ) ; 
 
    new_tree_node->element = data ;
    new_tree_node->parent = target_node ;
    new_tree_node->head = NULL ;
    new_tree_node->tail = NULL ;
    new_tree_node->node_count = 0 ; 

    new_link_node->node = new_tree_node ; 
    new_link_node->next = NULL ; 
    
    if (!(target_node->head)) {
        target_node->head = new_link_node;
    } else {
        target_node->tail->next = new_link_node;
    }
    target_node->tail = new_link_node;
    target_node->node_count++; 

    return new_tree_node ;
}


int children_nums( tree_node* target_node )
{
    return target_node->node_count ;
}


bool is_leaf( tree_node* target_node )
{
    if(target_node->head) return 0 ;
    return 1 ;
}


bool is_empty( tree_node* target_node )
{
    return ( target_node->parent == NULL && target_node->head == NULL && !(target_node->element) ) ;
}

bool has_brother( tree_node* target_node )
{
    if(target_node->parent->node_count >= 2) return 1 ;

    return 0 ;
}


void detach( tree_node* father , int target_element_of_son )
{
    linking_node* temp = father->head ;

    if(father->head == NULL){ 
        printf("Your target_node has no children_node ! \n\n") ;
        return ;
    }else if(father->head->next == NULL){  // only one child node :
        father->head->node->parent = NULL ;
        father->head = NULL ;
        father->tail = NULL ;
    }else{

            while ( temp->next->node->element != target_element_of_son )
            {
                temp = temp->next ;
            } 
            temp->next = temp->next->next ; 
         }
    

    father->node_count -- ;
}


void attach( tree_node* father , tree_node* son )
{
    linking_node* new_linking_node = (linking_node* )malloc( sizeof(linking_node) ) ;
    new_linking_node->next = NULL ;
    new_linking_node->node = son ; 

    if (father->tail == NULL || father->head == NULL){ 
        father->head = new_linking_node ;
        father->tail = new_linking_node ;
    }else{
        father->tail->next = new_link_node ;
        father->tail = new_linking_node;
        father->tail->node = son ; 
    }
    son->parent = father ;
    father->node_count ++ ;
}

int main()
{
    tree_node* root = (tree_node*)malloc( sizeof(tree_node) ); 
    root->element = 0;
    root->parent = NULL;
    root->head = NULL;
    root->tail = NULL;
    root->node_count = 0;
    
    tree_node* a1 = increase_node( root , 10 ) ;
    tree_node* a2 = increase_node( root , 20 ) ;
    tree_node* a3 = increase_node( root , 30 ) ; 
    tree_node* a1b1 = increase_node( a1 , 40 ) ;
    tree_node* a1b2 = increase_node( a1 , 50 ) ;
    tree_node* a3b1 = increase_node( a3 , 60 ) ; 

    tree_node* a1b1c1 = increase_node( a1b2 , 70 ) ;
    tree_node* a3b1c1 = increase_node( a3b1 , 80 ) ; 

    printf("\n") , traverse(root) , printf("\n\n")  ;
    
    // detect element in node :x
    printf("a1 :     %d \n" , a1->element );       
    printf("a1b2 :   %d \n" , a1b2->element );    
    printf("a1b1c1 : %d \n" , a1b1c1->element ); 
    printf("a3b1c1 : %d \n\n" , a3b1c1->element ); 

    printf("Does a1b2 has a brother node? answer : %d \n" , has_brother(a1b2) );
    printf("children number of a1 node: %d\tchildren number of a3b1: %d\n\n" , children_nums(a1) , children_nums(a3b1) ) ;
    printf("  Is a3b1c1 leaf node ?  answer : %d \n  Is a3 leaf node ?  answer : %d\n\n" , is_leaf(a3b1c1) , is_leaf(a1) );


    detach(a3 , 60) ;
    printf("  After detach a3b1 from a3 , Is a3 leaf node ?  answer : %d\n\n" , is_leaf(a3) );
    detach(a1b2 , 70) ;
    printf("  After detach a1b1c1 from a1b1 , Is a1b1 leaf node ?  answer : %d\n\n" , is_leaf(a1b2) );
    //rubustness :
    printf("Attempting to cut after a1b1 , who has no child node : \n") ;
    detach(a1b1 , 100000) ;
    
    attach( a3 , a3b1 ) ;

    printf("Attach a3 tree to a2 node : \n") ;    
    attach( a2 , a3 ) ; 
    printf("Having extended a2 tree !\nnow : a2--a3--a3b1--a3b1c1 ! " ) ;
    
    printf("\n") , traverse(root) , printf("\n\n")  ;

    free(root),free(a1),free(a2),free(a3),free(a1b1),free(a1b2),free(a3b1),free(a1b1c1),free(a3b1c1);
    return 0 ;
}

这段代码定义了一棵树的基本数据结构,以及树的基本操作函数。具体来说,代码中定义了两个结构体类型:tree_nodelinking_node,其中tree_node用于表示树的节点,linking_node用于表示连接树节点的链表节点。同时,定义了一系列操作树节点的函数,包括:

traverse( tree_node* target_node ):

这段代码定义了一个名为 traverse 的函数,该函数接受一个指向 tree_node 结构的指针作为参数。函数的主要作用是遍历以 target_node 为根的树,并打印每个节点的值。首先,函数会使用 printf 函数打印 target_node 节点的值。然后,函数会使用一个指向 linking_node 结构的指针 temp,并将其初始化为 target_node 的第一个子节点(如果有的话)接下来,函数进入一个循环中,该循环会一直执行,直到 temp 变量为空指针。在循环中,函数会递归地调用自身,传递 temp 的节点指针作为参数,以便打印 temp 子节点的值。然后,函数会将 temp 变量指向下一个兄弟节点,以便在下一次循环中处理。因此,traverse 函数将遍历以 target_node 为根的树,并按照深度优先的顺序输出每个节点的值。

increase_node(tree_node* target_node , int data ):

这段代码实现了向树中插入新节点的功能。它创建了一个新的树节点(new_tree_node),并将其与一个新的链表节点(new_link_node)关联。新树节点的数据为参数data,其父节点为目标节点target_node,其链表头和尾指针被初始化为NULL,并且节点计数为0。新链表节点的指针指向新树节点,其next指针初始化为NULL。如果目标节点的链表头为空,则将新链表节点设置为链表头。否则,将新链表节点添加到链表尾部。最后,更新目标节点的尾指针、节点计数,并返回新创建的树节点指针。总体来说,这段代码实现了树节点的动态添加功能,可以方便地构建一棵树。

children_nums( tree_node* target_node ):

这段代码实现了一个函数 children_nums,它接受一个指向树节点的指针 target_node 作为参数,并返回该节点的子节点数量。具体来说,它返回 target_nodenode_count 成员变量的值,即子节点的个数。

is_leaf( tree_node* target_node ):

这段代码定义了一个函数is_leaf,它接受一个tree_node类型的指针参数target_node,用于判断该节点是否为叶子节点。在函数内部,首先通过访问target_node->head判断该节点是否有子节点,如果有子节点则说明该节点不是叶子节点,返回0,否则该节点为叶子节点,返回1。可以看到,该函数的实现是基于对树节点结构的定义和对叶子节点的定义,根据这些信息来判断该节点是否为叶子节点。

is_empty( tree_node* target_node ):

这段代码是一个函数,用于判断一个树节点是否为空。它使用了三个条件:首先,这个节点必须没有父节点,即parent为NULL;其次,这个节点的head属性也必须为NULL,表示它没有子节点;最后,这个节点的element属性必须也为0,表示它不存储任何元素。当这三个条件都满足时,函数返回true,否则返回false。

has_brother( tree_node* target_node ):

这段代码实现了判断给定的树节点 target_node 是否有兄弟节点。首先判断 target_node 的父节点是否有至少两个子节点,如果有,则说明 target_node 一定有兄弟节点,返回 1 表示有兄弟节点;否则,说明 target_node 是父节点的唯一子节点,返回 0 表示没有兄弟节点。

detach( tree_node* father , int target_element_of_son ):

这段代码实现了从一棵树中移除一个特定子节点的功能。它接受一个父节点指针和一个要移除的子节点元素值作为参数。函数首先遍历父节点的所有子节点,寻找要删除的子节点的位置。如果该子节点不存在,函数将输出一条消息并退出。如果该子节点是父节点的唯一子节点,函数将简单地将父节点的头和尾指针都设置为NULL,以表示该父节点不再有任何子节点。如果有多个子节点,函数将在链表中找到目标子节点的前一个节点,并将其指向目标子节点的指针指向目标子节点的下一个节点,从而将目标子节点从父节点的子节点链表中分离出来。最后,父节点的节点计数器将递减1。

attach( tree_node* father , tree_node* son ):

这段代码实现了将一个子节点添加到父节点的子节点列表中。首先,通过动态内存分配来创建一个新的链式节点,即 new_linking_node,它包含一个指向子节点的指针。然后,代码检查父节点的子节点列表是否为空,如果是,则将新节点作为父节点的第一个子节点添加。否则,将新节点添加到子节点列表的末尾,同时更新父节点的 tail 指针。最后,将子节点的父指针设置为父节点,同时增加父节点的 node_count 值,表示它现在有一个新的子节点。

main函数中,首先新建了一个根节点root,然后以root为父节点,分别添加了数个子节点。最后,调用traverse函数遍历输出整棵树。

注意事项

学习C语言中的树数据结构时,我们需要注意确定树的存储方式,可以使用指针来表示树的节点,这样可以简单地使用动态内存分配来创建和管理节点。在创建节点时,需要为其分配内存,并在节点之间建立关联。还要理解树的遍历方式,树的遍历方式有前序遍历、中序遍历和后序遍历。在遍历树时,需要掌握如何递归访问节点和处理子节点。还要确定节点的数据类型,节点可以包含任意类型的数据,这取决于所要解决的问题。通常情况下,节点应该包含一个值和指向子节点的指针。还要注意确定树的结构,树可以是二叉树或非二叉树。二叉树是指每个节点最多有两个子节点的树,而非二叉树可以有任意数量的子节点。要熟悉树的常见操作,树的常见操作包括搜索、插入、删除和遍历。在实现这些操作时,需要掌握如何遍历树和处理节点之间的关系。还要注意内存管理,由于树的节点是使用动态内存分配来创建的,因此需要注意内存管理。在删除节点时,需要将其从父节点中移除,并释放其内存。要注意编写测试用例,为了确保代码正确性,需要编写测试用例来测试树的基本操作和功能。测试用例应该包括各种不同情况的输入和输出。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值