链式二叉树及二叉树各种接口的实现(C)

二叉树的性质
  1. 若规定根节点的层数为1,则一棵非空二叉树的第 i i i层上最多有 2 i − 1 2^{i-1} 2i1个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是 2 h − 1 2^{h}-1 2h1
  3. 对任何一棵二叉树,如果度为0其叶结点个数为 n 0 n_{0} n0, 度为2的分支结点个数为 n 2 n_{2} n2,则有 n 0 = n 2 + 1 n_{0}=n_{2}+1 n0=n2+1
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度, h = log ⁡ 2 ( n + 1 ) h=\log_{2}(n+1) h=log2(n+1)
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为 i i i的结点有:
    1. i > 0 i> 0 i>0 i i i位置节点的双亲序号: ( i − 1 ) / 2 (i-1)/2 (i1)/2 i = 0 i=0 i=0 i i i为根节点编号,无双亲节点
    2. 2 i + 1 < n 2i+1<n 2i+1<n,左孩子序号: 2 i + 1 2i+1 2i+1 2 i + 1 ≥ n 2i+1\ge n 2i+1n否则无左孩子
    3. 2 i + 2 < n 2i+2<n 2i+2<n,右孩子序号: 2 i + 2 2i+2 2i+2 2 i + 2 ≥ n 2i+2\ge n 2i+2n否则无右孩子
      ![[Pasted image 20240927172102.png]]
链式结构的实现

![[Pasted image 20240927174228.png]]

每棵树都可以分解成根节点,根节点的左子树,根节点的右子树
这样可以从根节点开始一直往下拆解,直到这棵树的左右两个子树都是空树停止

二叉树的遍历

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
    ![[Pasted image 20240927175114.png]]
  • 前序
    要求把树拆成根节点,左子树,右子树
    遍历顺序:
    1,{2,(3,N,N),(N)},{4,(5,N,N),(6,N,N)}
  • 中序
    左子树,根,右子树
    遍历顺序:
    {(N,3,N),2,(N)},1,{(N,5,N),4,(N,6,N)}
    遍历顺序
  • 后序
    左子树,右子树,根
    遍历顺序:
    {(N,N,3),(N),2},{(N,N,5),(N,N,6),4},1
  • 层序
    一层一层走
    1,2,4,3,5,6

普通的链式二叉树没有意义

二叉树树的实现

链式树的定义
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;
  • 有左右两个子树节点
  • 还有val表示节点存的值
创建节点
BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node = NULL){perror("malloc fail");exit(-1);	}node->val = x;node->left = NULL;node->right = NULL;return node;
}
  • malloc一个树节点的内存空间大小
  • 判断空间是否创建成功
  • 将节点的值置为x,左子树和右子树的指针置为空
二叉树的递归结构

递归调用展开图
![[Pasted image 20240929160449.png]]

先序中序后序遍历实现
void PrevOrder(BTNOde* root)
{if (root == NULL)return;printf("%d ", root->val);PrevOrder(root->left);PrevOrder(root->right);
}void InOrder(BTNOde* root)
{if (root == NULL)return;InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}void PostOrder(BTNOde* root)
{if (root == NULL)return;PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}
  • 通过递归实现遍历二叉树
  • 先实现返回条件,当目前子树的根节点是空时,返回
  • 这里是双路递推,先是递推左子树,再递推右子树,前中后遍历分别在两次递推的前中后访问根节点的值
节点个数
  1. 局部静态变量
int TreeSize(BTNode* root)
{static int size = 0;if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}

静态变量和全局变量存在于静态区,所以各个位置的size指同一个size,因为都是从静态区去取的,不是栈帧里面都有的
不会每次都走初始化,局部的静态成员变量只会执行一次
但是当多次调用这个函数时,size会累加,不符合预期。每次调用都要初始化为0
目前生命周期是全局的,作用域是局部的
这是不对的,这样的treesize是一次性的。
2. 手动初始化

int size = 0;int TreeSize(BTNode* root)
{if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}

在全局初始化size为0,之后调用的时候手动将size置为0
会发生限制安全问题
3. 变为递归子问题
遇到根节点,想求这棵树的节点个数,返回它的两棵子树的节点个数,接着找子树的子树,一直递归直到子树为空
树的节点的个数等于左子树节点的个数加右子树节点的个数再加自己

int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

![[Pasted image 20241001113806.png]]

叶子节点个数

节点是空,返回0
节点是叶子节点,返回1
都不是,返回左子树和右子树

int TreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

![[Pasted image 20241001130133.png]]

第k层的节点个数

当前树的第k层=左子树的第k-1层+右子树的第k-1层
一直到某一节点的第一层
如果为空,返回0;不为空,返回1

int TreeKLevel(BTNode* root, int level)
{assert(k > 0);if (root == NULL)return 0;if (k == 1){return 1;}return TreeKLever(root->left, k-1) + TreeKLever(root->right, k-1);
}

![[Pasted image 20241001140506.png]]

销毁

遇到一个根节点,先销毁左子树,再销毁右子树,回来再销毁自己

void TreeDestroy(BTNode* root)
{if (root == NULL){return;}TreeDestroy(root->left);TreeDestroy(root->right);free(root);
}

不需要置空,形参的改变不影响实参

查找

如果节点为空,返回空
如果节点的值等于x,返回节点
左子树找到就不用到右子树找了

//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;return TreeFind(root->left, x)|| TreeFind(root->right, x);
}

这样返回的不是节点的指针,返回的是x在不在,是空指针或1的结果
如果这个函数返回的是bool值,只判断真和假

BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;BTNode* ret = NULL;ret = TreeFind(root->left, x);if (ret)return ret;ret = TreeFind(root->right, x);if (ret)return ret;return NULL;
}

如果左边找到了,就return一下
如果没找到,就去右边找,找到了就return
如果右边也没找到,就返回空

  1. 若x=3
    ![[Pasted image 20241001185739.png]]

  2. 若x=4
    ![[Pasted image 20241001191416.png]]

类似写法

BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;BTNode* ret = NULL;ret = TreeFind(root->left, x);if (ret)return ret;return TreeFind(root->right, x);}
层序遍历

先进先出
根节点入队列,出队列的时候将它的两个左子节点和右子节点插入队列
上一层带下一层
因为要存的是树的指针,所以队列的数据类型要改为BTNode*
.h文件不会被编译,而会在.c文件里面展开,所以要包含队列的.h文件,include语句要在树的结构体的下面,这样.h文件可以找见树的定义

void LevelOrder(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->val);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);QueuePop(&q);}printf("\n");QueueDestroy(&q);
}
判断完全二叉树

完全二叉树走层序遍历的特征
完全二叉树在满二叉树的基础上,前n-1层都是满的,最后一层不满
但是最后一层,从左到右是连续的
如果这棵树是完全二叉树,这棵树层序遍历都是连续的
即层序:非空节点是连续的就是完全二叉树;非空节点是不连续,中间有空节点,就是非完全二叉树
要判断是否连续,层序遍历的时候,把NULL也入进去
非完全二叉树,出现空了以后,一定还有非空在队列里面

int TreeComplete(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);QueuePop(&q);}//已经遇到空节点,如果队列中后面还有非空,就不是完全二叉树while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}
  • pop了以后还可以访问front节点,pop删除的是队列的节点,不会影响树的节点
  • 队列里有指针指向树的节点
  • 取front,相当于用一个front指针指向队头的节点
  • 队列为空才能结束,不是到那个节点结束
返回树的高度

对于根节点而言,如果求出了左子树的高度和右子树的高度
树的高度等于左子树和右子树高的那一个加1
空树的时候,高度取0

int TreeHeight(BTNode* root)
{if (root == NULL)return 0;return TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

这样写运算消耗很大,会重复计算,每棵树都是这样
所以要把每次计算出来的值保存下来

int TreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1: rightHeight + 1;
}

将比较大小封装成一个函数

int TreeHeight(BTNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root-<right)) + 1;
}

没有重复计算

深度优先广度优先

深度优先遍历:前序遍历,递归
广度优先遍历:层序遍历,队列配合

声明定义分离实现

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node = NULL){perror("malloc fail");exit(-1);	}node->val = x;node->left = NULL;node->right = NULL;return node;
}void PrevOrder(BTNOde* root)
{if (root == NULL)return;printf("%d ", root->val);PrevOrder(root->left);PrevOrder(root->right);
}void InOrder(BTNOde* root)
{if (root == NULL)return;InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}void PostOrder(BTNOde* root)
{if (root == NULL)return;PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}//节点个数
int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}//叶子节点个数
int TreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}//第k层节点个数
int TreeKLevel(BTNode* root, int level)
{assert(k > 0);if (root == NULL)return 0;if (k == 1){return 1;}return TreeKLever(root->left, k-1) + TreeKLever(root->right, k-1);
}//二叉树销毁
void TreeDestroy(BTNode* root)
{if (root == NULL){return;}TreeDestroy(root->left);TreeDestroy(root->right);free(root);
}//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;TreeFind(root->left, x);TreeFind(root->right, x);
}void LevelOrder(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->val);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);QueuePop(&q);}printf("\n");QueueDestroy(&q);
}int TreeComplete(BTNode* root)
{Que q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);QueuePop(&q);}//已经遇到空节点,如果队列中后面还有非空,就不是完全二叉树while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}int TreeHeight(BTNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root-<right)) + 1;
}int main()
{//手动构建二叉树BTNode* node1 = BuyNode(1);BTNode* node1 = BuyNode(2);BTNode* node1 = BuyNode(3);BTNode* node1 = BuyNode(4);BTNode* node1 = BuyNode(5);BTNode* node1 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;PrevOrder(node1);printf("\n");InOrder(node1);printf("\n");PostOrder(node1);printf("\n");return 0;
}

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

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

相关文章

Koa学习

Koa 安装与配置 1. 初始化项目 在终端中执行以下命令&#xff1a; # 创建项目文件夹 mkdir koa cd koa# 初始化并安装依赖 npm init -y npm install koa npm install nodemon --save-dev2. 修改 package.json 在 package.json 文件中进行如下修改&#xff1a; {"type…

C语言 | Leetcode C语言题解之第472题连接词

题目&#xff1a; 题解&#xff1a; typedef struct Trie {struct Trie * children[26];bool isEnd; }Trie;#define TRIE_INITIAL(node) do { \for (int i 0; i < 26; i) { \(node)->children[i] NULL; \} \(node)->isEnd false; \ }while(0);static void freeTri…

互联网线上融合上门洗衣洗鞋小程序,让洗衣洗鞋像点外卖一样简单

随着服务创新的风潮&#xff0c;众多商家已巧妙融入预约上门洗鞋新风尚&#xff0c;并携手洗鞋小程序&#xff0c;开辟线上蓝海。那么&#xff0c;这不仅仅是一个小程序&#xff0c;它究竟蕴含着哪些诱人好处呢&#xff1f; 1. 无缝融合&#xff0c;双线共赢&#xff1a;小程序…

美团Java一面

美团Java一面 9.24一面&#xff0c;已经寄了 收到的第一个面试&#xff0c;表现很不好 spring bean生命周期 作用域&#xff08;忘完了&#xff09; 为什么用redis缓存 redis和数据库的缓存一致性问题 redis集群下缓存更新不一致问题 aop说一下 arraylist和linkedlist 数据库的…

2024年软件设计师中级(软考中级)详细笔记【5】软件工程基础知识上(分值10+)

第5章软件工程 目录 前言第5章 软件工程基础知识&#xff08;上&#xff09;&#xff08;分值10&#xff09;5.1 软件工程概述5.1.4 软件过程 5.2 软件过程模型5.2.1 瀑布模型 (Waterfall Model)5.2.2 增量模型5.2.3 演化模型5.2.4 喷泉模型&#xff08;Water Fountain Model&a…

JavaScript下载文件(简单模式、跨域问题、文件压缩)

文章目录 简介简单文件下载通过模拟form表单提交通过XMLHttpRequest方式 跨域(oss)下载并压缩文件完整示例文件压缩跨域设置 简介 相信各位开发朋友都遇到过下载的文件的需求&#xff0c;有的非常简单&#xff0c;基本链接的形式就可以。 有的就比较复杂&#xff0c;涉及跨域…

小米路由器R3Gv2安装openwrt记录

前言 小米路由器R3Gv2的硬件配置与小米路由器4A千兆版一致&#xff0c;但bootloader有所不同&#xff0c;因此openwrt的固件不要互刷。另外&#xff0c;R3Gv2和R3G、4A百兆版是不同的设备&#xff0c;切勿混淆。 硬件信息 OpenWrt参数页-Xiaomi MiWiFi 3G v2 CPU&#xff1a…

PPT分享:埃森哲-业务流程BPM能力框架体系

PPT下载链接见文末~ 业务流程管理&#xff08;BPM, Business Process Management&#xff09;的能力框架体系是一个全面、系统的流程管理方法论和工具集&#xff0c;旨在帮助企业优化和持续改进其业务流程&#xff0c;从而提升运营效率和市场竞争力。 一、BPM能力框架体系概述…

CUDA Example 处理一张二维图像

CUDA 实战 5.3.3 基于共享内存的位图&#xff1a;突出了同步操作的重要性 __synthreads() 才能保证图像的正确输出&#xff0c;如果去掉同步操作&#xff0c;输出图像如下&#xff1a; 加上同步操作之后&#xff1a; #include "cpu_bitmap.h" #include "cpu_an…

小北的技术博客:探索华为昇腾CANN训练营与AI技术创新——Ascend C算子开发能力认证考试(初级)

前言 哈喽哈喽友友们,这里是zyll~(小北)智慧龙阁的创始人及核心技术开发者。在技术的广阔天地里,我专注于大数据与全栈开发,并致力于成为这一领域的新锐力量。通过智慧龙阁这个平台,我期望能与大家分享我的技术心得,共同探索技术的无限可能。 Ascend C编程:小北的技术…

SKG未来健康校招社招入职测评:综合能力及性格问卷SHL测评题库

SKG未来健康科技股份有限公司在校招和社招过程中使用的SHL测评题库主要考察应聘者的综合能力和性格特征。以下是对这些测评的简要分析&#xff1a; 综合能力测评&#xff1a; 测评时间&#xff1a;46分钟&#xff08;实际答题时间36分钟&#xff09; 题目数量&#xff1a;30题…

【操作系统】深入探索:操作系统内核与用户进程的数据交互艺术

目录 一、数据从内核缓冲区拷贝到用户进程缓冲区&#xff0c;是谁来负责拷贝的&#xff0c;是操作系统还是用户进程&#xff1f;实际的执行者到底是谁&#xff1f;二、系统调用以及用户态内核态的相互转换1、系统调用2、用户态内核态的相互转换 三、如何形象的理解linux的虚拟地…

Django学习笔记十三:优秀案例学习

Django CMS 是一个基于 Django 框架的开源内容管理系统&#xff0c;它允许开发者轻松地创建和管理网站内容。Django CMS 提供了一个易于使用的界面来实现动态网站的快速开发&#xff0c;并且具有丰富的内容管理功能和多种插件扩展。以下是 Django CMS 的一些核心特性和如何开始…

Vue 脚手架学习

1.使用 Vue 脚手架 1.1 初始化脚手架 1.1.1 具体步骤 第一步&#xff08;仅第一次执行&#xff09;&#xff1a;全局安装vue/cli。 npm install -g vue/cli 第二步&#xff1a;切换到你要创建项目的目录&#xff0c;然后使用命令创建项目 vue create xxxx 第三步&#xff1a;启…

【CSS Tricks】鼠标滚轮驱动css动画播放,使用js还是css?

目录 引言一、js实现1. 实现思路2. 实现案例3. 看下效果 二、css实现1. 代码修改2. 属性介绍2.1 看下浏览器支持性2.2 常用属性值2.2.1 scroll&#xff08;&#xff09;2.2.2 view&#xff08;&#xff09; 三、总结 引言 本篇为css的一个小技巧 页面中的动画效果随着滚轮的转动…

React技术在Meta Connect 2024大会

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

架构与思维:漫谈高并发业务的CAS及ABA

1 高并发场景下的难题 1.1 典型支付场景 这是最经典的场景。支付过程&#xff0c;要先查询买家的账户余额&#xff0c;然后计算商品价格&#xff0c;最后对买家进行进行扣款&#xff0c;像这类的分布式操作&#xff0c;如果是并发量低的情况下完全没有问题的&#xff0c;但如果…

其他:python语言绘制案例

文章目录 介绍导入python包图1图2 介绍 python语言的科研绘图合集&#xff0c;数据来源Hydrogen-diffusion-and-water-rock-reaction 导入python包 import pandas as pd import glob import proplot as pplt import seaborn as sns import numpy as np import matplotlib.py…

基于 Prometheus+Grafana+Alertmanager 搭建 K8S 云监控告警平台(附配置告警至QQ、钉钉)

文章目录 一、机器规划二、部署安装 node-exporter、prometheus、Grafana、kube-state-metrics1、创建 monitor-sa 命名空间2、安装node-exporter组件2.1、说明2.2、应用资源清单2.3、通过node-exporter采集数据 3、k8s 集群中部署 prometheus3.1、创建一个 sa 账号3.2、将 sa …

element-ui的树形结构样式调整,添加线条和边框样式

element-ui的树形结构样式调整&#xff0c;添加线条和边框样式 先看图效果&#xff1a; <template><div class"temperature_monitoring"><div class"temperature_monitoring_left"><div class"tree-container"><e…