非线性表-BiTree(特殊二叉树-需记忆!!!!难啊难)
1:哈夫曼树-wpl最小-最优二叉树
1.没有度为1的结点 2.n个叶子节点的哈夫曼树共有2n-1个结点 树的特点:度为2结点和叶结点的关系n2=n0-1 所以:当叶结点为n时,度为二的结点数为n-1 因为哈夫曼没有度为一的结点,所以一共在树中有2n-1个结点 3.哈夫曼树任意非叶结点的左右子树交换后还是哈夫曼树 4.对同一组权值{w1,w2,...,wn},是会存在不同结构的哈夫曼树 BADCADFEED 法一:直接传递字符的ASCII码,每个字符占八位,一共传递80位 法二:我们发现数据只是从A-F,一共6个字符,我们完全可以使用3位二进制来表示这些数据(网络对方需要知道我们的编码才能解码) A B C D E F 000 001 010 011 100 101 001000011010000011101100100011(30) 法三:我们发现一段文字中各个数字出现的频率是不一样的,各个字母频率相加100%,可以使用哈夫曼编码,对数据再次进行压缩 假设各个字母频率为 A 27,B 8,C 15,D 15,E 30,F 5 1.先构造哈夫曼树 2.获取前缀码 (1)左右分支分别用0,1表示(避免了二义性) (2)字符只在叶子节点 1001010010101001000111100(25)
2:平衡二叉树
看这个(重点看构造平衡二叉树时左右子树旋转方法)https://www.cnblogs.com/ssyfj/p/9504996.html
//返回二叉树的深度,前提T存在 int depth(bitTree T) { if(!T) return 0; else return max(depth(T->lchild),depth(T->rchild))+1; //这有个缺点,空树会返回深度1 } //判断平衡二叉树 int balance(bitTree t) { int left,right; int cha; if(t!=NULL) { left=depth(t.lchild); right=depth(t.rchild); cha=left-right; if(cha>1||cha<-1) { return false; } return balance(t.lchild)&&balance((t.rchild)); } }
3:二叉搜索树
看这个(重点看插入删除时树的变化)https://www.cnblogs.com/ssyfj/p/9503740.html
这里“查找”文章里也有叙述:https://www.cnblogs.com/Y-flower/p/15528200.html
4:线索二叉树
在这里:https://www.cnblogs.com/ssyfj/p/9465532.html
//线索二叉树 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef char TElemType; typedef int Status; typedef enum {Link,Thread} PointerTag; //二叉树的二叉链表结点结构定义 typedef struct BiTNode //结点结构 { TElemType data; //结点数据 struct BiTNode *lchild, *rchild; //左右孩子指针 PointerTag ltag; PointerTag rtag; }BiThrNode, *BThriTree;
//按照前序输入二叉树中结点的值(一个字符)#表示空树,构造二叉树表示二叉树T void CreateBiThrTree(BThriTree *T) { TElemType ch; scanf("%c", &ch); if (ch == '#') *T = NULL; else { *T = (BThriTree)malloc(sizeof(BiThrNode)); if (!*T) exit(ERROR); (*T)->data = ch; //生成根节点数据 (*T)->ltag = Link; (*T)->rtag = Link; CreateBiThrTree(&(*T)->lchild); //构造左子树 CreateBiThrTree(&(*T)->rchild); //构造右子树 } }
线索化
//在线索化前设置头结点 InOrderThreading(BThriTree* p,BThriTree T) { *p = (BThriTree)malloc(sizeof(BiThrNode)); (*p)->ltag = Link; (*p)->rtag = Thread; //按树的结构设置头结点 //使他指向自己,下面判断是不是空树,再进行指向 (*p)->rchild = *p; if (!T) //T是空树的情况 { (*p)->lchild = *p; } else { //使得头结点指向根节点 (*p)->lchild = T; pre = *p; //对所有的含有空指针域的结点进行线索化,从根节点开始,不包含头结点 InThreading(T); //线索化后将中序遍历最后的结点指向头结点 pre->rchild = *p; pre->rtag = Thread; (*p)->rchild = pre; } } BThriTree pre; //始终指向前驱结点 //进行线索化 //中序遍历线索化 void InThreading(BThriTree T) { if (T) { InThreading(T->lchild); //递归左子树线索化 //进行线索化操作 if (!T->lchild) //若是没有左孩子,进行线索化 { T->ltag = Thread; //前驱线索 T->lchild = pre; //左孩子指针指向前驱 } //我们这里是想要对该T结点进行线索化 /* if (!T->rchild) { T->rtag = Thread; T->rchild = 下一个后继结点 } */ //但是我们无法知道下一个后继结点位置 //我们可以反向思考,我们根据当前节点去推断前驱结点是不是可以向右线索化,来配置 if (!pre->rchild) //前驱没有右孩子 { pre->rtag = Thread; //后继线索 pre->rchild = T; //前驱有孩子指向后继(当前节点p) } pre = T; //将前驱推移,始终保持pre执行p的前驱 InThreading(T->rchild); //递归右子树线索化 } }
线索树遍历
//我们是使用中序方法来线索化二叉树,所以使用中序来遍历是最方便的 void InOrderTraverse(BThriTree T) //我们传入的是线索化带有头结点的树 { BThriTree p; p = T->lchild; //这是根节点 while (p != T) //可以直接按照双向链表来处理二叉树了 { while (p->ltag == Link) //当我们找到了Thread时就是找到了最左边的结点,是双向链表的开始结点 { p = p->lchild; } printf("%c", p->data); p = p->rchild; printf("%c", p->data); p = p->rchild; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?