C语言实现的树

在分层次组织在管理上便于查找

(Tree)是n(n>=0)个结点的有限集合,当n=0时称为空树

  • 树中有一个称为根(Root)的特殊结点,用r表示
  • 其余节点可分为多个互不相交的子集,每个子集又可以作为一棵树,称为根的子树

数的基本术语

结点的度(Degree):与结点相关的子树的个数
树的度:树的节点中最大的度数
叶结点(Leaf):度为0的结点
父节点(Parent):若一个结点含有子结点,则这个结点称为其子结点的父结点
子节点(Child):一个结点含有的子树的根结点称为该结点的子节点
兄弟节点(Sibling):具有相同父节点的结点互称为兄弟节点
路径和路径长度:从根结点到叶结点所经过的结点构成一条路径,路径上的结点数目称为路径长度
祖先节点(Ancestor):从根到当前结点的路径上所有结点
子孙节点(Descendant):从当前结点到根的路径上所有结点
节点的层次(Level):从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的深度(Depth):树中节点的最大层次就是树的深度

查找

静态查找:集合中记录是固定的,查找时不需要修改集合
动态查找:集合中记录是变化的,除了查找还可能发生插入和删除

静态查找

顺序查找
typedef struct LNode *List;
struct LNode{
    ElemType Element[MAXSIZE];
    int length;
};
int SequentialSearch(List Tbl, ElemenType K){//在Element中查找关键词为K的数据元素
    int i;
    Tbl->Element[0] = K;//建立哨兵
    for(i=Tbl->Length;Tbl->Element[i]!=K;i++);
    retrun i;//返回找到的元素在Element中的位置,找不到返回0
}
二分查找
int BinarySearch(List Tbl, ElemenType K){//在表Tbl中查找关键词为K的数据元素
    int left, right, mid, NoFound = -1;

    left = 1;//初始左边界
    right = Tbl->Length;//初始右边界
    while(left<=right){
        mid = (left+right)/2;//计算中间位置
        if(K<Tbl->Element[mid]){
            right = mid-1;
        }//调整右边界
        else if(K>Tbl->Element[mid]){
            left = mid+1;//调整左边界
        }
        else{
            return mid;//找到,返回mid
        }
    }
    return NoFound;//找不到,返回-1
}

二叉树

二叉树:每个结点最多有两个子树的树结构

几种特殊的二叉树

  • 斜二叉树
  • 完美二叉树
  • 完全二叉树

二叉树的几个重要性质

一个二叉树第i层的最大节点数: 2 i − 1 2^{i-1} 2i1,i>=1
深度为k的二叉树最多有 2 k − 1 2^k-1 2k1个节点,k>=1
任意一棵二叉树,其叶子结点个数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1

二叉树的抽象数据类型描述

数据对象集:一个有穷的节点集合
操作集:BT∈BinTree,Item∈ElementType

Boolean IsEmpty(BinTree BT)判别BT是否为空树
void Traversal(BinTree BT)遍历BT,按某顺序访问每一个节点
BinTree CreatBinTree()创建二叉树

常用的二叉树遍历方法
void PreOrderTraversal(BinTree BT)先序遍历—根、左子树、右子树
void InOrderTraversal(BinTree BT)中序遍历—左子树、根、右子树
void PostOrderTraversal(BinTree BT)后序遍历—左子树、右子树、根
void LevelOrderTraversal(BinTree BT)层次遍历—根、左子树、右子树

二叉树的存储结构

顺序存储结构

完全二叉树:按从上至下,从左至右的顺序存储

  • 非根节点i的父节点为 [ i / 2 ] [i/2] [i/2]
  • 左孩子节点为 2 i 2i 2i(若 2 i < = n 2i<=n 2i<=n,则无左孩子)
  • 右孩子节点为 2 i + 1 2i+1 2i+1(若 2 i + 1 < = n 2i+1<=n 2i+1<=n,则无右孩子)
链式存储结构
typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
    ElementType Data;
    BinTree Left;
    BinTree Right;
};

二叉树的递归遍历

先序遍历

遍历过程:

  1. 访问根结点
  2. 遍历左子树
  3. 遍历右子树
void PreOrderTraversal(BinTree BT){
    if(BT){
        printf("%d ", BT->Data);
        PreOrderTraversal(BT->Left);
        PreOrderTraversal(BT->Right);
    }
}
中序遍历

遍历过程:

  1. 遍历左子树
  2. 访问根结点
  3. 遍历右子树
void InOrderTraversal(BinTree BT){
    if(BT){
        InOrderTraversal(BT->Left);
        printf("%d ", BT->Data);
        InOrderTraversal(BT->Right);
    }
}
后序遍历

遍历过程:

  1. 遍历左子树
  2. 遍历右子树
  3. 访问根结点
void PostOrderTraversal(BinTree BT){
    if(BT){
        PostOrderTraversal(BT->Left);
        PostOrderTraversal(BT->Right);
        printf("%d ", BT->Data);
    }
}

二叉树的非递归遍历

中序非递归遍历

遇到一个节点,就把他压栈,并去遍历他的左子树
当左子树遍历结束后,从栈顶弹出这个节点,并访问这个节点
然后按其右指针再去中序遍历该节点的右子树

void InOrderTraversal(BinTree BT){
    BinTree T = BT;
    Stack S = CreatStack(MAXSIZE);//创建并初始化栈
    while(T || !IsEmpty(S)){
        while(T){//一直向左并将沿途节点压栈
            Push(S,T);
            T = T->Left;
        }
        if(!IsEmpty(S)){
            T = Pop(S);//弹出栈顶节点
            printf("%d ", T->Data);//访问弹出的节点
            T = T->Right;//中序遍历右子树
        }
    }
}
先序非递归遍历
void PreOrderTraversal(BinTree BT){
    BinTree T = BT;
    Stack S = CreatStack(MAXSIZE);//创建并初始化栈
    while(T || !IsEmpty(S)){
        while(T){//一直向左并将沿途节点压栈
            Push(S,T);
            printf("%d ", T->Data);//访问弹出的节点
            T = T->Left;
        }
        if(!IsEmpty(S)){
            T = Pop(S);//弹出栈顶节点
            T = T->Right;//中序遍历右子树
        }
    }
}

层序遍历

队列实现:遍历从根节点开始,将根节点入队列,然后出队一个节点,将该节点的左右孩子入队,如此往复

先根节点入队

  1. 从队列中取出一个元素
  2. 访问该元素所指结点
  3. 若该节点左右孩子不为空,则将该节点的左右孩子入队
void LevelOrderTraversal(BinTree BT){
    Queue Q;
    BinTree T;
    if(!BT) return;//空树
    Q = CreatQueue(MAXSIZE);//创建并初始化队列
    AddQ(Q,BT);//根节点入队
    while(!IsEmpty(Q)){
        T = DeleteQ(Q);//出队一个节点
        printf("%d ", T->Data);//访问该节点
        if(T->Left) AddQ(Q, T->Left);//将该节点的左孩子入队
        if(T->Right) AddQ(Q, T->Right);//将该节点的右孩子入队
    }
}

遍历二叉树的应用

输出二叉树中的叶子结点

在二叉树的遍历算法中增加检测节点的“左右子树是否都为空”的判断条件

void PreOrderTraversal(BinTree BT){
    if(BT){
        if(!BT->Left && !BT->Right){
            printf("%d ", BT->Data);
        }
        PreOrderTraversal(BT->Left);
        PreOrderTraversal(BT->Right);
    }
}
求二叉树的高
int PostOrderGetHeight(BinTree BT){
    int HL, HR, MaxH;
    if(BT){
        HL = PostOrderGetHeight(BT->Left);//递归求左子树的高度
        HR = PostOrderGetHeight(BT->Right);//递归求右子树的高度
        MaxH = (HL>HR)?HL:HR;//求得左右子树中高的一侧
        return (MaxH+1);//返回当前节点的高度
    }
    else{
        return 0;//空树的高度为0
    }
}
二元运算表达式树及其遍历

在这里插入图片描述

三种遍历方式:

中序遍历得到的中缀表达式:a+bc+(de+f)g
先序遍历得到的前缀表达式:++a
bc*+defg
后续遍历得到的后缀表达式:abc
+def+g+

由两种遍历序列

必须有中序遍历

先序和中序遍历序列
根据先序遍历序列第一个节点确定根节点
根据根节点在中序遍历序列中分割出左右两个子序列
对左子树和右子树分别递归使用相同的方法继续分解

在这里插入图片描述

二叉搜索树

二叉搜索树(Binary Search Tree,BST)又称为二叉查找树、二叉排序树

可以为空,如果不为空,则满足

  1. 非空左子树中的所有结点值均小于根结点的值
  2. 非空右子树中的所有结点值均大于根结点的值
  3. 左右子树均为二叉搜索树

操作集
Position Find(ElementType X, BinTree BST)从二叉搜索树BST中查找元素X,返回X在BST中的位置
Position FindMin(BinTree BST)从二叉搜索树BST中查找最小元素,返回该元素的指针
Position FindMax(BinTree BST)从二叉搜索树BST中查找最大元素,返回该元素的指针
BinTree Insert(ElementType X, BinTree BST)将元素X插入二叉搜索树BST中,并返回BST
BinTree Delete(ElementType X, BinTree BST)从二叉搜索树BST中删除元素X,并返回BST

二叉搜索树的查找

查询从根节点开始,如果树为空,返回NULL

若搜索树非空,则根节点关键字与X比较,并进行不同的处理

  1. 若X小于根节点键值,只需在左子树中继续查找
  2. 如果X大于根节点键值,只需在右子树中继续查找
  3. 若两者比较结果是相等,搜索完成,返回该节点

在这里插入图片描述

尾递归

Position Find(ElementType X, BinTree BST){
    if(!BST) return NULL;//空树
    if(X > BST->Data){
        return Find(X, BST->Right);//在右子树中继续查找
    }
    else if(X < BST->Data){
        return Find(X, BST->Left);//在左子树中继续查找
    }
    else{//X == BST->Data
        return BST;//找到
    }
}

迭代函数

Position IterFind(ElementType X, BinTree BST){
    while(BST){
        if(X > BST->Data){
            BST = BST->Right;//在右子树中继续查找
        }
        else if(X < BST->Data){
            BST = BST->Left;//在左子树中继续查找
        }
        else{//X == BST->Data
            return BST;//找到
        }
    }
    return NULL;//空树
}
查找最大和最小元素

最大元素一定是在二叉搜索树的最右端
最小元素一定是在二叉搜索树的最左端

查找最小元素

Position FindMin(BinTree BST){
    if(!BST) return NULL;//空树
    else if(!BST->Left) return BST;//左子树为空,则当前节点就是最小元素
    else return FindMin(BST->Left);//在左子树中继续查找
}

查找最大元素

Position FindMax(BinTree BST){
    if(BST){
        while(BST->Right) BST = BST->Right;//一直向右走,直到右子树为空
        return BST;//返回当前节点
    }
}
二叉搜索树的插入
BinTree Insert(ElementType X, BinTree BST){
    if(!BST){
        /* 若为空树,生成并返回一个节点的二叉搜索树*/
        BST = malloc(sizeof(struct TreeNode));
        BST->Data = X;
        BST->Left = BST->Right = NULL;
    }else{ //开始找要插入的位置
        if(X < BST->Data){
            BST->Left = Insert(X, BST->Left);
            //递归插入左子树
        }else if(X > BST->Data){
            BST->Right = Insert(X, BST->Right);
            //递归插入右子树
        }
    }
    return BST;
}
二叉搜索树的删除

要删除的是叶节点:直接删除,再修改其父节点指针设置为NULL
要删除的节点只有一个孩子节点:将其父节点的指针指向要删除节点的孩子节点
要删除的节点有左右两个子树:用另一节点代替要删除的节点(左子树的最大节点或右子树的最小节点)

BinTree Delete(ElementType X, BinTree BST){
    Position Tmp;
    if(!BST) printf("要删除的节点不存在\n");
    else if(X < BST->Data){
        BST->Left = Delete(X, BST->Left);//左子树递归删除
    }else if(X > BST->Data){
        BST->Right = Delete(X, BST->Right);//右子树递归删除
    }
    else{
        //X == BST->Data
        //找到了要删除的节点
        if(BST->Left && BST->Right){
            //左右子树都存在
            Tmp = FindMin(BST->Right);//在右子树中找最小节点
            BST->Data = Tmp->Data;//将最小节点值赋值给当前节点
            BST->Right = Delete(Tmp->Data, BST->Right);//删除最小节点
        }else{
            //左右子树只有一个
            Tmp = BST;
            if(!BST->Left) BST = BST->Right;//左子树为空,将右子树赋值给当前节点
            else BST = BST->Left;//右子树为空,将左子树赋值给当前节点
            free(Tmp);//释放当前节点
        }
    }
    return BST;
}

平衡二叉树

搜索树节点不同插入次序,将导致不同的深度和平均查找长度ASL

在这里插入图片描述

平衡因子(Balance Factor,简称BF):BF(T) = |BF(T->Left) - BF(T->Right)|
即BF(T)是左右子树的高度差

平衡二叉树(AVL树):空树或每个节点的平衡因子绝对值不超过1的二叉搜索树,即BF(T) <= 1

平衡二叉树的调整

RR插入,需要RR旋转(右旋)

在这里插入图片描述

LL插入,需要LL旋转(左旋)

在这里插入图片描述

Huffman树(最优二叉树)

带权路径长度(Weighted Path Length,WPL):设二叉树有n个叶子节点,每个叶子结点带有权值 W k W_k Wk,从根节点到每个叶子节点的长度为 L k L_k Lk,则二叉树带权路径长度为: W P L = ∑ k = 1 n W k × L k WPL = \sum_{k=1}^n W_k \times L_k WPL=k=1nWk×Lk

Huffman树的构造

每次把权值最小的两颗二叉树合并,得到一颗新的二叉树,新的二叉树的根节点的权值为两颗原二叉树根节点权值的和

typedef struct TreeNode *HuffmanTree;
struct TreeNode{
    int Weight;
    HuffmanTree Left, Right;
};

HuffmanTree Huffman(MinHeap H){
    //假设H->Size个权值已经存在H->Elements[]->Weight中
    int i;
    HuffmanTree T;
    BuildMinHeap(H);//将H->Elements[]中的元素按最小堆排序
    for(i = 1; i < H->Size; i++){
        //做H->Size-1次合并
        T = malloc(sizeof(struct TreeNode));//生成新节点
        T->Left = DeleteMin(H);//从最小堆中删除一个节点,作为新节点的左子树
        T->Right = DeleteMin(H);//从最小堆中删除一个节点,作为新节点的右子树
        T->Weight = T->Left->Weight + T->Right->Weight;//新节点的权值等于左右子树权值之和
        Insert(H, T);//将新节点插入最小堆
    }
    T = DeleteMin(H);//最小堆中只剩一个节点,就是最终的Huffman树
    return T;
}
Huffman树的特点

没有度为1的节点
n个叶子节点的Huffman树有2n-1个节点
Huffman树的任意非叶子节点的左右子树交换后仍是Huffman树
对同一权值{ W 1 , W 2 , . . . , W n W_1, W_2, ..., W_n W1,W2,...,Wn},存在不同构的Huffman树,但是WPL相同

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值