二叉树 -- Tree

数据结构 – 二叉树



一、定义

说明

  1. 是一种特殊的树,每个节点最多有两个子节点(左节点、右节点)。
  2. 有序二叉树(最核心的二叉树),需要掌握。

下图为有序二叉树,从根节点开始,大小左右排列:

在这里插入图片描述

二、三种遍历方式

  1. 先序遍历:
    处理节点自己的数据 -> 处理左节点 -> 处理右节点

  2. 中序遍历(重点掌握):
    处理左节点 -> 处理节点自己的数据 -> 处理右节点

  3. 后序遍历:
    处理左节点 -> 处理右节点 -> 处理自己的节点

三、数据结构

  1. 定义 Tree 结构,由两部分组成:

定义描述节点的数据结构:

typedef struct node{
    int data;//数据
    struct node *left;//左子树
    struct node *right;//右子树
}node_t;

声明整棵树的数据结构:

typedef struct tree{
    node_t *root;//根节点地址
    int cnt;//节点个数
}tree_t;

三、功能函数

  1. create_node:创建新的节点,malloc 动态创建:
static node_t *create_node(int data)
{
    //分配内存
    node_t *pnode=(node_t *)malloc(sizeof(node_t));
    //初始化节点
    pnode->data=data;
    pnode->left=NULL;
    pnode->right=NULL;
    return pnode;
}
  1. insert_data:向根节点 Tree 中插入新节点 pnode:

定义子函数:proot为根节点,pnode为新节点;

/* 请查看二级指针定义,否则是内存空间上的复制 */
/* 需要针对tree进行操作 */
/* 过程中使用迭代操作,请留意 */
void insert(node_t **proot,node_t *pnode)
{
    //递归函数的退出条件:根节点为空(root,L,R)
    if(NULL==*proot){        //当树只有根节点时
         *proot=pnode;
          return ;   
    }
    //插入到左子树中
    if((*proot)->data > pnode->data){
        insert(&(*proot)->left,pnode);    
        return;
    }else {  //插入到到右子树
        insert(&(*proot)->right,pnode);
        return;
    }  
}

向有序二叉树创建新的节点;

void insert_data(tree_t *tree,int data)
{
    //创建新的节点
    node_t *pnode=create_node(data);
    //调用递归函数进行插入,注意递归法则
    insert(&tree->root,pnode);
    //更新个数
    tree->cnt++; 
}

在这里插入图片描述

  1. travel_data:中序遍历

定义子函数 : travel

void travel(node_t *proot)
{
    //递归函数的结束判断
    if(proot != NULL){
        /*中序遍历*/
        travel(proot->left);//左节点
        printf("%d",proot->data);//处理节点自己
        travel(proot->right); //右节点 
} 

定义调用函数:

void travel_data(tree_t *tree)
{
    //调用递归函数遍历
    travel(tree->root);
    printf("\n");
}

注意 ---- 该遍历过程中需要重点理解递归的流程:

在这里插入图片描述

  1. clear_data:清空二叉树

定义子函数:清除节点的递归函数

void clear(node_t **proot)
{
    if(*proot != NULL)
    {
        //清空左子树
        clear(&(*proot)->left);
        //清空右子树
        clear(&(*proot)->right);
        //释放节点内存
        free(*proot);
        *proot=NULL;    
    }    
}

定义调用函数:

void clear_data(tree_t *tree)
{
     clear(&tree->root);
     tree->cnt=0;   
}

注意----该遍历过程中需要重点理解递归的流程:

在这里插入图片描述

  1. del_data:删除某个节点

定义 find 子函数:在根节点下查找数据

说明:寻找方法同样使用递归,递推查找。

node_t **find(node_t **pproot,int data)
{
    //1. 如果树为空,直接返回
    if(NULL == *pproot){
        return pproot;    
    }
    //2. 比较节点和目标值,若相等返回节点
    if(data == (*pproot)->data){
        return pproot;    
    }
    //3. 如果目标值小于节点的值,左子树找寻
    if(data < (*pproot->data)){
        return find(&(*pproot)->left,data);    
    }
    else {
        return find(&(*pproot)->right,data);  //右子树找              
    }    
}

定义 find_data 调用函数

说明: 传递tree树节点中root的值,root为树节点指向下一节点的节点地址,使用 & 和前面类似,避免内存空间的复制,对root的改变会做用到 本tree;
注意:返回值为 ** 二级指针,表明找到data后,data这个节点就是新的树节点。

 // 传递整个树
node_t **find_data(tree_t *tree,int data)  
{
    //调用递归函数从root开始查找要删除的节点并返回这个节点的地址
    return find(&tree->root,data);    //传递树的首节点
}

定义 del_data 删除节点函数
删除节点的第四点好好体会,其实就是做暂存,然后free掉;

void del_data(tree_t *tree,int data)
{
    //1. 首先要找到删除的节点,返回这个节点的首地址
    node_t **ppnode=find_data(tree,data);
    if (NULL == *ppnode){
        printf("要寻找的目标节点不存在");
        return;    
    }
    
    //2. 如果找到要删除的节点,将节点的左子树插入到右子树
    if((*ppnode)->left != NULL){
            insert(&(*ppnode)->right,(*ppnode)->left);
    }
    
    //3. 将要删除节点的右子树给到要删除节点的父节点的左子树
    node_t *ptemp = *ppnode; //临时暂存
    *ppnode=(*ppnode)->right;
    
    //4. 释放内存
    free(*ptemp);
    *ptemp = NULL; //好习惯
    
    //5. 更新计数
    tree->cnt--;
}
  1. modify_data:修改节点数据;
/*修改元素*/
void modify_data(tree_t *tree,int old_data,int new_data)
{
    //删除旧结点
    del_data(tree,old_data);
    //插入新的节点
    insert_data(tree,new_data);
}
  1. main 函数:功能函数写好之后咱们看看调用;
int main(void)
{
    //1. 种树
    tree_t tree;
    
    //2. 初始化=NULL
    tree.root=NULL
    tree.cnt=0
    
    //3. 添加新节点
     insert_data(&tree,50);

     //采用中序遍历方式
     travel_data(&tree);
     
     //删除40
     del_data(&tree,40);

     //将10修改成250
     modify_data(&tree,10,250);

     return 0;
}

总结

数据结构形式不在多,在于深入理解他在做什么,好好理解树tree

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
因权限不够,只能上传20M,故分两部分上传 提供了有关使用算法数据结构的一个详尽的介绍。Bucknall先从算法性能的讨论开始,涵盖了诸如数组、链表和二叉树等内容。这本书强调了查找算法(如顺序和二分查找),另外也重点介绍了排序算法(包括冒泡排序、插入排序、希尔排序、快速排序和堆排序),此外还提供了有关的优化技术。不仅如此,作者还介绍了散列和散列表、优先队列、状态机和正则表达式以及诸如哈夫曼和LZ77等数据压缩技术。 随附光盘中有作者所开发的一个相当成功的自由软件库EZDSL,另外还有可运行于各版本Delphi上和Kylix上的源代码,此外还提供了TurboPower Software公司的可执行程序。 目录 前言 致谢 第1章什么是算法 1.1什么是算法 1.2算法和平台 1.3调试与测试 1.4小结 第2章数组 2.1数组 2.2Delphi中的数组类型 2.3TList类和指针数组 2.4磁盘数组 2.5小结 第3章链表、栈和队列 3.1单链表 3.2双向链表 3.3链表的优缺点 3.4栈 3.5队列 3.6小结 .第4章查找 4.1比较例程 4.2顺序查找 4.3二分查找 4.4小结 第5章排序 5.1排序算法 5.2排序基础知识 5.3小结 第6章随机算法 6.1随机数生成 6.2其他随机数分布 6.3跳表 6.4小结 第7章散列和散列表 7.1散列函数 7.2利用线性探测方法实现冲突解决 7.3其他开放定址机制 7.4利用链式方法解决冲突 7.5利用桶式方法解决冲突 7.6磁盘上的散列表 7.7小结 第8章二叉树 8.1创建一个二叉树 8.2叉树的插入和删除 8.3二叉树的遍历 8.4二叉树的类实现 8.5二叉查找树 8.6伸展树 8.7红黑树 8.8小结 第9章 优先队列和堆排序 9.1优先队列 9.2堆 9.3堆排序 9.4扩展优先队列 9.5小结 第10章 状态机和正则表达式 10.1状态机 10.2正则表达式 10.3小结 第11章数据压缩 11.1数据表示 11.2数据压缩 11.3位流 11.4最小冗余压缩 11.5字典压缩 11.6小结 第12章 高级主题 12.1读者-写者算法 12.2生产者-消费者算法 12.3查找两文件的差别 12.4小结 后记
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值