【数据结构】-树形结构概述和操作

一、树形结构概述

        树形结构是数据结构中的一种逻辑关系结构,在此逻辑结构中数据元素之间满足一对多的分层结构关系,也称分层结构。

        1.所有的数据节点中的起始节点,有且仅有一个,也称为根节点。

                没有直接前驱,可以有多个直接后继节点。

        2.末尾节点,可以有一个或者多个,也称为叶节点,或者树叶。

                每一个节点没有直接后继节点,有且仅有一个直接前驱节点。

        3.中间的节点,也称为内部节点。

                每一个节点有且仅有一个直接前驱节点;可以有多个直接后继节点。

二、树的定义

        树是n个节点的有序集合,它满足两个条件:

                1.有且仅有一个特定的根节点;

                2.其余节点可以分为m个互不相交的有序集合T1,T2,......Tm,其中每个集合又是一棵树,称为其根的子树。

三、二叉树的定义

        二叉树是n个节点的有限集合,它或者是空集,或者是由一个根节点以及两棵互不相交、分别称为左右子树的二叉树组成。二叉树与普通有序树不同,二叉树严格区分左右孩子,即使只有一个子节点也会区分。

四、实例:

        创建如下图的二叉树,分别输出层次遍历、先序遍历、中序遍历、后序遍历;

        所谓的遍历,指的是沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次;

        对于二叉树的遍历方式:主要分为递归遍历和分层遍历实现。

        在对于二叉树的遍历过程中,如果去根节点,左子树和右子树也是二叉树,可以采用相同的遍历思路实现对左子树和右子树遍历过程。所以在对于二叉树的遍历可以采用递归遍历算法实现。

具体实现:

        1.树和队列结构类型定义

//节点数据域类型定义
typedef int data_t;
//节点数据类型的定义
typedef struct binTree{
    data_t data;//结点本身数据
    struct binTree *lchild;//左孩子节点空间地址
    struct binTree *rchild;//右孩子节点空间地址
}binTree_t;
//链队列定义
typedef struct node{
    binTree_t *addr;
    struct node *next;
}node_t;
typedef struct linkqueue{
    node_t *front;
    node_t *rear;
}linkqueue_t;

 2.创建二叉树和队列

//创建完全二叉树
binTree_t * CreateBinTree(int i,int n)
{
    binTree_t *btree;
    //创建根节点
    btree = malloc(sizeof(binTree_t));
    if(btree == NULL)
        return NULL;

    memset(btree,0,sizeof(binTree_t));
    btree->data = i; //根节点数据域存储的是节点编号
    //左子树设置
    if(2*i<=n)
        btree->lchild = CreateBinTree(2*i,n);
    else
        btree->lchild = NULL;
    
    //右子树设置
    if(2*i+1<=n)
        btree->rchild = CreateBinTree(2*i+1,n);
    else
        btree->rchild = NULL;

    return btree;
}
//创建链队列
linkqueue_t * CreateLinkQueue()
{
    linkqueue_t *queue;
    queue = malloc(sizeof(linkqueue_t));
    if(queue == NULL)
        return NULL;
    queue->front = malloc(sizeof(node_t));
    if(queue->front == NULL)
    {
        free(queue);
        return NULL;
    }
    queue->rear = queue->front;

    return queue;
}

 三、队列操作

//入队
int EnLinkQueue(linkqueue_t * queue,binTree_t *myaddr)
{
    node_t * p;
    if(queue == NULL)
        return -1;
    //创建入队结点并初始化
    p = malloc(sizeof(node_t));
    if(p == NULL)
        return -1;
    p->addr = myaddr;
    //入队
    p->next = NULL;
    queue->rear->next = p;
    queue->rear = p;

    return 0;
}
//获得队列元素
int GetLinkQueue(linkqueue_t *queue,binTree_t **myaddr)
{
    if(queue == NULL)
        return -1;
    if(queue->front->next == NULL)
        return -1;
    *myaddr = queue->front->next->addr;

    return 0;
}
//出队
int DeLinkQueue(linkqueue_t *queue)
{
    node_t *p;
    if(queue  == NULL)
        return -1;
    if(queue->front->next == NULL)
        return -1;
    //出队
    p = queue->front->next;
    queue->front->next = p->next;
    free(p);
    //出队的结点为最后一个结点
    if(queue->front->next == NULL)
        queue->rear = queue->front;
    
    return 0;
}
//判断链队列为空
int isEmptyLinkQueue(linkqueue_t *queue)
{
    return (queue->front == queue->rear);
}

四、分层遍历

//分层遍历
void layer_order(binTree_t * btree)
{
    binTree_t *myaddr;
    //创建空队列
    linkqueue_t * queue;
    queue = CreateLinkQueue();
    if(queue == NULL)
        return;
    //遍历根节点,并将节点入队
    printf("%d ",btree->data);
    EnLinkQueue(queue,btree);

    while(!isEmptyLinkQueue(queue))
    {
        GetLinkQueue(queue,&myaddr);
        DeLinkQueue(queue);
        //遍历左子节点
        if(myaddr->lchild != NULL)
        {
            printf("%d ",myaddr->lchild->data);
            EnLinkQueue(queue,myaddr->lchild);
        }
        //遍历右子节点
        if(myaddr->rchild != NULL)
        {
            printf("%d ",myaddr->rchild->data);
            EnLinkQueue(queue,myaddr->rchild);
        }
    }
    printf("\n");
}

五、树的遍历

//前序遍历
void pre_order(binTree_t * btree)
{
    if(btree == NULL)
        return ;
    printf("%d ",btree->data);//遍历根节点
    pre_order(btree->lchild);//遍历左节点
    pre_order(btree->rchild);//遍历右节点
}
//中序遍历
void mid_order(binTree_t * btree)
{
    if(btree == NULL)
        return ;
    pre_order(btree->lchild);//遍历左节点
    printf("%d ",btree->data);//遍历根节点
    pre_order(btree->rchild);//遍历右节点
}
//后序遍历
void after_order(binTree_t * btree)
{
    if(btree == NULL)
        return ;
    pre_order(btree->lchild);//遍历左节点
    pre_order(btree->rchild);//遍历右节点
    printf("%d ",btree->data);//遍历根节点
}

六、主函数调用

int main()
{
    binTree_t *btree;
    //创建完全二叉树:根节点编号为1,最大节点编号为12
    btree = CreateBinTree(1,12);
    if(btree == NULL)
        return -1;

    //分层遍历
    layer_order(btree);
    //先序遍历
    pre_order(btree);
    printf("\n");
    //中序遍历
    mid_order(btree);
    printf("\n");
    //后序遍历
    after_order(btree);
    printf("\n");
    return 0;
}
### Java 中红黑树的数据结构实现与应用 #### 红黑树简介 红黑树是一种自平衡二叉查找树,在计算机科学中用于高效地存储检索键值对。该数据结构的特点在于其能够自动调整节点位置来维持一定的平衡条件,从而保证最坏情况下的时间复杂度为 O(log n)[^1]。 #### 平衡特性对比 相较于 AVL 树而言,尽管两者都致力于维护二叉查找树的高度接近于最小可能高度,但是由于红黑树对于旋转次数的要求较低,因此在频繁发生插入或删除的情况下表现更优;而像 `TreeMap` `TreeSet` 这样的集合类正是利用这一点实现了高效的增删改查功能[^2]。 #### 关键属性定义 为了满足上述提到的性质,每棵红黑树中的结点除了拥有指向左右子树以及父辈指针外还需要额外记录颜色信息(红色/黑色),具体如下所示: ```java private static final boolean RED = false; private static final boolean BLACK = true; static class Node<K,V> { K key; // 存储的关键字 V value; // 对应关键字所映射的对象 Node<K,V> left; // 左孩子引用 Node<K,V> right; // 右孩子引用 Node<K,V> parent;// 父亲引用 boolean color; // 颜色标记 public Node(K key, V value, Node<K,V> parent){ this.key=key; this.value=value; this.parent=parent; this.color=RED; // 新加入的节点默认设为红色 } } ``` #### 插入操作逻辑 当向一棵已有的红黑树内添加新元素时,会先按照普通的 BST 方式找到合适的位置并创建新的叶子节点。之后再依据一系列规则判断是否需要执行重涂色或是旋转变换以恢复整棵树应有的形态特征。 - 如果当前新增加的是根,则直接将其变为黑色; - 若祖父存在且叔叔也为红色,则将父亲、叔叔均改为黑色并将祖父变更为红色继续向上处理; - 当仅有单侧兄弟呈红色状态时可通过一次左(右)转使得双亲成为临时性的“假”根以便后续统一调整; - 剩余情形下只需简单交换父子间染色即可完成修复工作。 以上过程可以概括成一段伪代码形式表示出来: ```pseudo function insertFixup(node) if node is root then set its color to black and return. while (node != null && node's parent is red) // 处理违反红黑树特性的几种场景... end while ``` 实际上完整的算法较为复杂,涉及到多个分支路径的选择问题,这里仅给出大致思路供理解参考之用。 #### 删除操作概述 移除某个指定项的过程同样遵循着相似的原则——即先依照普通二叉搜索树的方式定位目标对象所在位置,并考虑三种不同类型的状况分别采取相应的措施加以应对:如果待消除的目标恰好处于末端处那么可以直接摘掉它而不影响其他部分;反之则需寻找合适的替代品填补空缺后再做进一步讨论。 值得注意的是在整个过程中可能会引入一些暂时不符合标准的情况,这就要求我们及时作出必要的修正动作直至最终达到稳定为止。 #### 应用实例分析 正如前面提及的一样,红黑树广泛应用于各种编程语言的标准库当中作为内部机制支撑起诸如有序表之类的抽象容器类型。例如在 JDK8 版本以后版本里头,`HashMap` 的链地址法解决哈希冲突方案便采用了基于红黑树优化后的链表结构,以此提高极端情况下访问效率的同时兼顾了空间利用率方面的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值