树与二叉树学习笔记

树与二叉树

  • 计算机中的树
    • 树的概念
    • 树的类型
  • 什么是二叉树
    • 二叉树:定义与特点
    • 二叉树:前序、中序、后序遍历
    • 二叉树:深度、广度优先遍历
    • 二叉树:线索化
    • 二叉树:序列化与反序列化
  • haffman树
    • 平均编码长度
    • 构建haffman树
    • haffman树:代码演示

计算机中的树

树的概念

  计算机中的树与现实生活中的树结构是类似的,但是计算机中的树型结构更加抽象化。在计算机中的树型结构具备以下几个特点:

  • 除了根节点,每个结点都具有唯一父节点。
  • 每个结点可以有若干个子节点。
  • 树型结构具备层次关系
  • 边与结点之间,具备无循环的特性。

树的类型

  • 二叉树(Binary Tree):

    每个节点最多有两个子节点,通常称为左子节点和右子节点。

  • 多叉树

    每个节点最多不止有两个子节点,比如说字典树、和双数组字典树。

    在这里插入图片描述

什么是二叉树

二叉树:定义与特点

  • 每个结点最多只有两个子节点
  • 遍历和结构操作方便
  • 满足递归的基本性质,相关操作可以借助递归方式完成。
  • 二叉树在计算机领域应用广泛。

二叉树:前序、中序、后序遍历

  • 前序遍历

所谓前序遍历,指的就是遍历顺序按照:根、左、右 的方式进行。

在这里插入图片描述

	// typedef struct Node {//   int val;// 	 struct Node *left, *right;	// } Node;void preOrder(Node *root) {if (root == NULL) return ;printf("%d ", root->val);preOrder(root->left);preOrder(root->right);return ;}
  • 中序遍历

中序遍历的遍历顺序按照:左、根、右 的方式进行。

在这里插入图片描述

// typedef struct Node {
//   int val;
// 	 struct Node *left, *right;	
// } Node;void inOrder(Node *root) {if (root == NULL) return ;inOrder(root->left);printf("%d ", root->val);inOrder(root->right);return ;
}
  • 后序遍历

后序遍历的遍历顺序是按照:左、右、根的方式

在这里插入图片描述

// typedef struct Node {
//   int val;
// 	 struct Node *left, *right;	
// } Node;void lastOrder(Node *root) {if (root == NULL) return ;lastOrder(root->left);lastOrder(root->right);printf("%d ", root->val);return ;
}	

二叉树:深度、广度优先遍历

  • 深度优先遍历(DFS)

深度优先遍历,其遍历方式就是,沿着其中一条路径一直走到底,当无路可走时,就需要按照原来的路径回退一步,继续搜索其他结点,重复原来的操作。(不撞南墙不回头)

在这里插入图片描述

// typedef struct Node {
//    int key;
//    struct Node *lchild, *rchild;
//} Node;int tot = 0;void dfs(Node *root) {if (root == NULL) return ;int l, r;l = tot;tot += 1;dfs(root->lchild);dfs(root->rchild);r = tot; printf("%d : l[%d] | r[%d]\n", root->key, l, r);return ;
}
  • 广度优先遍历(BFS)

广度优先遍历,也可称之为层序遍历,因为将树型结构看成是一个金子塔,那么BFS就是从顶向底层遍历。

在这里插入图片描述

// typedef struct Node {
//    int key;
//    struct Node *lchild, *rchild;
//} Node;#define MAX_QUEUE 15void bfs(Node *root) {if (root == NULL) return ;Node *array[MAX_QUEUE] = {0};#undef MAX_QUEUEint head = 0, tail = 0;array[tail++] = root;while (tail != head) {Node *tmp = array[head++];if (tmp->lchild) array[tail++] = tmp->lchild;if (tmp->rchild) array[tail++] = tmp->rchild;printf("%d ", tmp->key);}printf("\n");return ;
}

二叉树:线索化

二叉树的线索化,其实就是将左右子节点为空的结点,重复利用起来,为特定的遍历方式,建立线索化,这样在完成某一个遍历顺序时,可以达到线性复杂度的效率。摆脱递归式遍历的资源浪费问题。

  • 中序遍历线索化

中序遍历的线索化,可以将当前结点中子节点为空的结点指向中序遍历的前驱或者后继结点,即当左孩子为空时,则指向中序遍历中,当前结点的前驱结点,当右孩子为空时,则指向中序遍历中,当前结点的后继结点。

在这里插入图片描述

  • 中序遍历线索化建立
 //typedef struct Node {//   int key;//    int ltag, rtag;//   struct Node *lchild, *rchild;// } Node;Node *pre_node = NULL, *in_node = NULL;
void __build_thread(Node *root) {if (root == NULL) return ;if (root->ltag == 0) __build_thread(root->lchild);if (in_node == NULL) in_node = root;if (pre_node && root->lchild == NULL) {root->lchild = pre_node;root->ltag = 1;}if (pre_node && pre_node->rchild == NULL) {pre_node->rchild = root;pre_node->rtag = 1;}pre_node = root;if (root->rtag == 0) __build_thread(root->rchild);return ;
}void build_thread(Node *root) {__build_thread(root);pre_node->rchild = NULL;pre_node->rtag = 1;return ;
}
  • 中序线索化遍历
 //typedef struct Node {//   int key;//    int ltag, rtag;//   struct Node *lchild, *rchild;// } Node;Node *getNextNode(Node *root) { if (root->rtag == 1) return root->rchild;root = root->rchild;while (root->ltag == 0 && root->lchild) root = root->lchild;return root;
}

二叉树:序列化与反序列化

  • 序列化(Serialize)

二叉树的序列化,指的是将二叉树的表示方法转化为字符串形式,即广义表表示方法。

在这里插入图片描述

//序列化
#define MAX_CHR 1000char buff[MAX_CHR + 5];
int len = 0;void serialize(Node *root) {if (root == NULL) return ;len += sprintf(buff + len, "%d", KEY(root));if (root->lchild == NULL && root->rchild == NULL) return ;len += sprintf(buff + len, "(");serialize(root->lchild);len += sprintf(buff + len, ",");serialize(root->rchild);len += sprintf(buff + len, ")");return ;
}
  • 反序列化(Deserialize)

二叉树的反序列化,就是将字符串(广义表形式)表示的二叉树又转换为树节点的形式。由于二叉树满足递归的性质,因此可以借助栈完成反序列化

在这里插入图片描述

//反序列化Node *deserialize(char *str) {Node *root = NULL, *preNode = NULL;Node **array = (Node **)malloc(sizeof(Node *) * MAX_CHR);int top = -1, scode = 0, flag = 0;for (int i = 0; str[i]; i++) {switch (scode) {case 0: {//读取关键词if (str[i] <= '9' && str[i] >= '0') {scode = 1;//读取数字} else if (str[i] == '(') {scode = 2;//入栈新元素} else if (str[i] == ',') {scode = 3;//修改flag} else if (str[i] == ')') {scode = 4;//完成栈顶元素的弹出}i -= 1;} break;case 1: {//读取数字int num = 0;while (str[i] <= '9' && str[i] >= '0') {num = num * 10 + (str[i] - '0');i++;}preNode = getNewNode(num);if (root == NULL) {root = preNode;} if (top != -1 && flag == 0) {array[top]->lchild = preNode;}if (top != -1 && flag == 1) {array[top]->rchild = preNode;}scode = 0;i -= 1;} break;case 2: {//入栈新元素 "("array[++top] = preNode;flag = 0;scode = 0;} break;case 3: {//遇到逗号 ","flag = 1;scode = 0;} break;case 4: {//出栈 ")"--top;flag = 0;scode = 0;} break;}}return root;
}

haffman树

平均编码长度

  • 定长编码:指的是无论是何种字符,每个字符的编码都是固定长度。
  • 变长编码:即每种字符的编码长度都不是固定的,
  • 问题1:这两种编码方式哪种更加适合用于网络传输?
  • 问题2:如何衡量两种编码方式的优劣?

我们知道最重要的衡量标准,就是单位时间内网络传输数据量的大小之分。假设两种编码方式,都是在相同网络传输环境下,哪种编码方式传输效率更高呢?不言而喻,就是平均编码长度最小的编码方式。

a v g ( L ) = ∑ L i × P i avg(L) = \sum{L_i \times P_i} avg(L)=Li×Pi

avg(L)表示平均编码长度 = 每一种字符的编码长度与对应字符出现概率的乘积累加和

构建haffman树

  • 首先,统计得到每一种字符的概率
  • 每次将最低频率的两个结点合并成一颗子树
  • 经过了n - 1轮合并,就得到了一颗哈夫曼树
  • 按照左0右1的形式,将编码读取出来

haffman树:代码演示

  • 完整代码
/*************************************************************************> File Name: 04.haffmantree.cpp> Author: > Mail: > Created Time: Wed 17 Jul 2024 10:23:16 AM CST************************************************************************/#include<stdio.h>
#include<stdlib.h>
#include<string.h>#define MAX_CHR 128typedef struct Node {char ch;int freq;struct Node *lchild, *rchild;
} Node;Node *getNewNode(char ch, int freq) {Node *p = (Node *)malloc(sizeof(Node));p->ch = ch;p->freq = freq;p->lchild = p->rchild = NULL;return p;
}int findMinNode(Node **arr, int n) {int ind = 0;for (int i = 1; i <= n; i++) {if (arr[ind]->freq > arr[i]->freq ) ind = i;}return ind;
} void swap_node(Node **arr, int i, int j) {Node *tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;return ;
}Node *buildHaffManTree(Node **arr, int n) {for (int i = 1; i < n; i++) {//find two nodeint ind1 = findMinNode(arr, n - i);swap_node(arr, ind1, n - i);int ind2 = findMinNode(arr, n - i - 1);swap_node(arr, ind2, n - i - 1);//build new nodeint freq_tmp = arr[n - i]->freq + arr[n - i - 1]->freq;Node *new_node = getNewNode(0, freq_tmp);new_node->lchild = arr[n - i - 1];new_node->rchild = arr[n - i];arr[n - i - 1] = new_node;}return arr[0];
}char *ch_code[MAX_CHR + 5] = {0};
int k = 0;void extractHaffManTree(Node *root, char buff[], int k) {buff[k] = 0;if (root->lchild == NULL && root->rchild == NULL) {ch_code[root->ch] = strdup(buff);return ;}buff[k] = '0';extractHaffManTree(root->lchild, buff, k + 1);buff[k] = '1';extractHaffManTree(root->rchild, buff, k + 1);return ;
}void clear(Node *root) {if (root == NULL) return ;clear(root->lchild);clear(root->rchild);free(root);return ;
}int main() {int n;char s[10] = {0};scanf("%d", &n);Node **array = (Node **)malloc(sizeof(Node *) * n);for (int i = 0; i < n; i++) {int freq;scanf("%s%d", s, &freq);array[i] = getNewNode(s[0], freq);}Node *root = buildHaffManTree(array, n);char buff[1000] = {0};extractHaffManTree(root, buff, 0);for (int i = 0; i < MAX_CHR; i++) {if (ch_code[i] == NULL) continue;printf("%c : %s\n", i, ch_code[i]);}clear(root);#undef MAX_CHRreturn 0;
}
  • 运行效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数学建模算法汇总(全网最全,含matlab案例代码)

数学建模常用的算法分类 全国大学生数学建模竞赛中&#xff0c;常见的算法模型有以下30种&#xff1a; 最小二乘法数值分析方法图论算法线性规划整数规划动态规划贪心算法分支定界法蒙特卡洛方法随机游走算法遗传算法粒子群算法神经网络算法人工智能算法模糊数学时间序列分析马…

大模型应用—大模型赋能网络爬虫

大模型赋能网络爬虫 简单来说,网页抓取就是从网站抓取数据和内容,然后将这些数据保存为XML、Excel或SQL格式。除了用于生成潜在客户、监控竞争对手和市场研究外,网页抓取工具还可以用于自动化你的数据收集过程。 借助AI网页抓取工具,可以解决手动或纯基于代码的抓取工具的…

shell脚本语言的入门

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

腾讯技术创作特训营 -- SUPERWINNIE -- AI重塑社交内容

目录 1 什么是AI社交内容 2 案例拆解 3 用LLM做爆文选题 4 用LLM出爆文脚本提示词 1 什么是AI社交内容 任何一个因素被AI取代都是AI社交内容 2 案例拆解 数字人 资讯素材 录屏产品的素材&#xff08;小红书测试AI产品&#xff09; 脚本 素材 剪辑 3 用LLM做爆文选题 &…

帝国神话怎么样使用服务器开服(Myth Of Empires)

1、购买后登录服务器&#xff08;百度莱卡云&#xff09; 进入控制面板后会出现正在安装的界面&#xff0c;安装大约5分钟&#xff08;如长时间处于安装中请联系我们的客服人员&#xff09; 2、创建端口 一共需要用到三个端口&#xff08;游戏端口&#xff0c;查询端口&#x…

【刷题汇总 -- 压缩字符串(一)、chika和蜜柑、 01背包】

C日常刷题积累 今日刷题汇总 - day0181、压缩字符串(一)1.1、题目1.2、思路1.3、程序实现 2、chika和蜜柑2.1、题目2.2、思路2.3、程序实现 3、 01背包3.1、题目3.2、思路3.3、程序实现 -- dp 4、题目链接 今日刷题汇总 - day018 1、压缩字符串(一) 1.1、题目 1.2、思路 读完…

vue3 vue页面根目录增加注释 keep-alive 不生效 需避开此位置

已参考官方文档如下&#xff0c;进行配置 Built-in Components | Vue.js 对应页面中配置 name defineOptions({name: "Users",inheritAttrs: false, }); 从Vue Devtools可以看到组件名字变成了Users&#xff0c;如下&#xff1a; 已配置一致后&#xff0c;仍不生…

【Linux】进程信号 --- 信号保存

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

【医学影像】RK3588+FPGA:满足远程诊疗系统8K音视频编解码及高效传输需求

医学影像 提供基于Intel平台、NXP平台、Rockchip平台的核心板、Mini-ITX主板、PICO-ITX主板以及工业整机等计算机硬件。产品板载内存&#xff0c;集成超高清编码/解码视频引擎&#xff0c;具有出色的数据处理能力和图形处理能力&#xff0c;功能高集成&#xff0c;可应用于超声…

【教学类-69-01】20240721铠甲勇士扑克牌涂色(男孩篇)

背景需求&#xff1a; 【教学类-68-01】20240720裙子涂色&#xff08;女孩篇&#xff09;-CSDN博客文章浏览阅读250次。【教学类-68-01】20240720裙子涂色&#xff08;女孩篇&#xff09;https://blog.csdn.net/reasonsummer/article/details/140578153 前期制作了女孩涂色延…

直播带货|主播、运营怎么考核 怎么分钱

在直播公司中&#xff0c;有两个非常重要的岗位&#xff1a;直播运营和主播。那么&#xff0c;直播公司应该如何正确发放工资呢&#xff1f;许多公司为这两个岗位的工资发放采取的是销售额提成的方式。 销售额提成存在一个问题&#xff1a;直播公司很容易通过广告费来刺激销售额…

MT6825磁编码IC在智能食品包装设备的应用

艾毕胜马达控制平台专家 MT6825磁编码IC&#xff0c;作为一款先进的传感器解决方案&#xff0c;在智能食品包装设备中的应用正日益广泛 MT6825磁编码IC&#xff0c;作为一款先进的传感器解决方案&#xff0c;在智能食品包装设备中的应用正日益广泛。它凭借卓越的性能和可靠性…

Windows及Linux系统加固

君衍. 一、Windows加固1、配置简介2、账户配置3、本地配置4、安全设置 二、Linux加固1、配置简介2、网络配置3、日志和审计配置4、访问认证和授权配置5、系统运维配置 一、Windows加固 1、配置简介 通常在Windows安全配置中有两类对象 一类是Windows Server&#xff0c;如win …

功能测试与APPSCAN自动化测试结合的提高效率测试策略

背景 手工探索性测试&#xff08;Manual Exploratory Testing&#xff0c;简称MET&#xff09;是一种软件测试方法&#xff0c;它依赖于测试人员的直觉、经验和即兴发挥来探索应用程序或系统。与传统的脚本化测试相比&#xff0c;手工探索性测试不遵循固定的测试脚本&#xff0…

敲详细的springboot中使用RabbitMQ的源码解析

这里介绍的源码主要是涉及springboot框架下的rabbitmq客户端代码&#xff08;具体在springframework.amqp.rabbit包下&#xff0c;区分一下不由springboot直接接管的spring-rabbit的内容&#xff09;&#xff0c;springboot基于RabbitMQ的Java客户端建立了简便易用的框架。 sp…

重生之我在学数据结构——队列

一.队列的概念 队列是一种先进先出(First In First Out &#xff0c;FIFO)的数据结构&#xff0c;可以简单理解为排队的概念。在队列中&#xff0c;数据项按照插入的顺序排列&#xff0c;并且只能在队列的一端插入&#xff08;称为队尾&#xff09;&#xff0c;在另一端删除&a…

品牌策划新手指南:如何让你的品牌脱颖而出?

品牌策划&#xff0c;简单来说&#xff0c;就是为品牌制定一套全方位的成长和发展计划&#xff0c;就像给品牌设计一条成长路线图&#xff0c;让它能够更好地吸引顾客、建立信任、提升知名度&#xff0c;最终实现销售和市场份额的增长。 品牌策划是什么&#xff1f; 想象一下…

【性能优化】在大批量数据下使用 HTML+CSS实现走马灯,防止页面卡顿(一)

切换效果 页面结构变化 1.需求背景 项目首页存有一个小的轮播模块,保密原因大概只能这么展示,左侧图片右侧文字,后端一次性返回几百条数据(开发环境下,生产环境只会更多).无法使用分页解决,前端需要懒加载防止页面卡顿 写个小demo演示,如下 2.解决思路 获取到数据后,取第一…

二、链表(2)

24. 两两交换链表中的节点 法一&#xff1a;迭代&#xff0c;while循环&#xff0c;注意要获取next给变量&#xff0c;得先判断非null, 需要4个变量&#xff0c; n0是前&#xff0c;n1 n2是交换的两&#xff0c;n3是n2的下一个可能为空&#xff0c;这种先把变量保存起来&#…

Leetcode1688. 比赛中的配对次数

问题描述&#xff1a; 给你一个整数 n &#xff0c;表示比赛中的队伍数。比赛遵循一种独特的赛制&#xff1a; 如果当前队伍数是 偶数 &#xff0c;那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛&#xff0c;且产生 n / 2 支队伍进入下一轮。如果当前队伍数为 奇…