【数据结构与算法】二叉树(Binary Tree)

相关推荐:堆(Heap) / 堆排序(HeapSort) / TopK

文章目录

  • 1.树
    • 1.1 树相关概念
    • 1.2 举例树的应用
  • 2. 二叉树
    • 2.1 二叉树分类
    • 2.2 特殊的二叉树
    • 2.3 二叉树的存储结构
  • 3. 二叉树实现与热门问题

1.树

树是一种非线性的数据结构,它看起来像一棵倒挂的树,根朝上而叶子朝下。下图是一棵二叉树,每个节点最多只有两个孩子节点。
在这里插入图片描述

1.1 树相关概念

在这里插入图片描述

  1. 根节点:如上图A节点就是根节点。
  2. 节点的度:一个节点含有的子树的个数,如上图A节点的度为6,F节点的度为3。
  3. 叶节点:度为0的节点,如上图B、C、H、I…等节点为叶节点。
  4. 父节点:也叫双亲节点,如上图E是I和J的父节点,其它节点同理。
  5. 子节点:也叫孩子节点,如上图B、C、D、E等是A的孩子节点。
  6. 兄弟节点:具有相同父节点的节点,如上图P、Q是兄弟节点。
  7. 树的度:最大的节点的度称为树的度,如上图树的度为6。
  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。
  9. 树的高度或深度:树中节点的最大层次,如上图树的高度为4。
  10. 森林:由m(m>0)棵互不相交的树的集合称为森林。
    在这里插入图片描述

1.2 举例树的应用

Linux的文件系统:
在这里插入图片描述
Windows的文件系统也是多叉树,和Linux文件系统不同的是Windows的文件系统可以是森林(至少分了两个盘)。
在这里插入图片描述

2. 二叉树

2.1 二叉树分类

普通的二叉树是没有意义的,存储数据并不比链表或数组好,真正让二叉树有意义的是二叉搜索树(又称二叉排序树或二叉查找树)、AVL树(平衡二叉树)、B树和红黑树。

二叉搜索树:简单地说就是左孩子节点比父节点小、右孩子节点比父节点大的树,二叉树的好处是遍历很快,从名字也能得出它是干嘛的。
在这里插入图片描述
不过极端的二叉搜索树则会造成很多浪费,不仅树另一边节点存储无效的NULL值,最要命的是遍历的时间复杂度增高。
在这里插入图片描述
平衡二叉树二叉树可以解决二叉搜索树的缺点,是其升级版,另外还有B树、红黑树等,感兴趣的小伙伴自行了解或看我其它相关文章,否则本篇文章会占用大量篇幅。

2.2 特殊的二叉树

  1. 满二叉树:每层的节点都是满的。
  2. 完全二叉树:假设树的高度是h,前h-1层的节点都是满的,最后一层也就是h层的节点不一定满,但一定要是有序。满二叉树也是一种特殊的完全二叉树,另外 (heap)也是完全二叉树,想了解的可以看这篇文章:堆(Heap)。
    在这里插入图片描述

2.3 二叉树的存储结构

二叉树可以使用两种结构存储:顺序结构或链式结构,说白了就是数组或链表。

不过对于二叉树而言基本都是用链表存储,因为用数组存储二叉树会浪费很多空间,而极端的二叉树搜索树更甚。
在这里插入图片描述
只有完全二叉树/完全二叉树/堆才适合用数组存储,其特性就决定了不会浪费数组空间,为什么非完全二叉树用数组存储会浪费内存空间,而完全二叉树不会,具体了解请看 文章:堆(Heap)。

C语言表示二叉树的结构:

typedef int valtype;
typedef struct TreeNode {
	valtype val;
	struct TreeNode* left;
	struct TreeNode* right;
} TreeNode;

3. 二叉树实现与热门问题

由于普通的二叉树插入删除操作都是没有意义的,所以这里不实现这种操作;另外由于二叉树通常使用链式存储,所以很多操作都是通过递归实现

申明:

#pragma once

#include <stdio.h>
#include <stdlib.h>

typedef int valtype;
typedef struct TreeNode {
	valtype val;
	struct TreeNode* left;
	struct TreeNode* right;
} TreeNode;

TreeNode* NewNode(valtype val);

// 前、中、后序遍历
void PreOrder(TreeNode* root);
void InOrder(TreeNode* root);
void PostOrder(TreeNode* root);

// 树节点个数
size_t TreeSize(TreeNode* root);
// 叶子节点个数
size_t TreeLeafSize(TreeNode* root);
// 树高度/深度
size_t TreeHeight(TreeNode* root);
// 第k层节点个数
size_t TreeKthSize(TreeNode* root, int k);

实现:

#define _CRT_SECURE_NO_WARNINGS 1

#include "BinaryTree.h"

TreeNode* NewNode(valtype val) {
	TreeNode* treeNode = (TreeNode*)malloc(sizeof(TreeNode));
	if (treeNode == NULL) {
		perror("BuyNode malloc failed.\n");
		exit(-1);
	}
	treeNode->val = val;
	treeNode->left = NULL;
	treeNode->right = NULL;
	return treeNode;
}

void PreOrder(TreeNode* root) {
	if (root != NULL) {
		printf("%d ", root->val);
		PreOrder(root->left);
		PreOrder(root->right);
	}
}

void InOrder(TreeNode* root) {
	if (root != NULL) {
		InOrder(root->left);
		printf("%d ", root->val);
		InOrder(root->right);
	}
}

void PostOrder(TreeNode* root) {
	if (root != NULL) {
		PostOrder(root->left);
		PostOrder(root->right);
		printf("%d ", root->val);
	}
}

size_t TreeSize(TreeNode* root) {
	return root == NULL // 本身 + 左子树 + 右子树节点之和
		? 0 : 1 + TreeSize(root->left) + TreeSize(root->right);
}

size_t TreeLeafSize(TreeNode* root) {
	if (root == NULL) {
		return 0;
	}
	// 非叶子结点的左子树和右子树的叶子节点之和
	return root->left == NULL && root->right == NULL
		? 1 : TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

size_t TreeHeight(TreeNode* root) {
	if (root == NULL) {
		return 0;
	}
	// 选出左子树和右子树中较高的树 + 根节点本身高度
	return max(TreeHeight(root->left), TreeHeight(root->right)) + 1;

	//size_t left = TreeHeight(root->left);
	//size_t right = TreeHeight(root->right);
	//return (left > right ? left : right) + 1; 
}

size_t TreeKthSize(TreeNode* root, int k) {
	if (root == NULL || k < 1) {
		return 0;
	}
	else if (k == 1) { 
		return 1; // 第k层
	}
	else { // k > 1 向下走直到来到第k层
		return TreeKthSize(root->left, k-1) + TreeKthSize(root->right, k-1);
	}
}

测试:

#define _CRT_SECURE_NO_WARNINGS 1

#include "BinaryTree.h"

static void BuildTree(TreeNode* root);

int main() {
	TreeNode root; 
	BuildTree(&root);

	PreOrder(&root);
	printf("\n");
	InOrder(&root);
	printf("\n");
	PostOrder(&root);
	printf("\n");

	printf("TreeSize = %zd\n", TreeSize(&root));
	printf("TreeLeafSize = %zd\n", TreeLeafSize(&root));
	printf("TreeHeight = %zd\n", TreeHeight(&root));
	printf("TreeKthSize = %zd\n", TreeKthSize(&root, 3));
	return 0;
}

static void BuildTree(TreeNode* root) {
	TreeNode* node2 = NewNode(2);
	TreeNode* node3 = NewNode(3);
	TreeNode* node4 = NewNode(4);
	TreeNode* node5 = NewNode(5);
	TreeNode* node6 = NewNode(6);
	TreeNode* node7 = NewNode(7);
	root->val = 1;
	root->left = node2;
	root->right = node4;
	node2->left = node3;
	node2->right = node7;
	node4->left = node5;
	node4->right = node6;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PKI - 05 证书申请步骤

文章目录 Pre概述第一步:时间同步第二步: 部署证书服务器第三步: 客户端产生密钥第四步: 验证证书服务器第五步: 申请个人证书第六步&#xff1a; 审核并签名证书第七步: 颁发数字证书第八步: 交换公钥 Pre PKI - 02 对称与非对称密钥算法 PKI - 03 密钥管理&#xff08;如何…

SpringBoot和SpringMVC

目录 一、springboot项目 &#xff08;1&#xff09;创建springboot项目 &#xff08;2&#xff09;目录介绍 &#xff08;3&#xff09;项目启动 &#xff08;4&#xff09;运行一个程序 &#xff08;5&#xff09;通过其他方式创建和运行springboot项目 二、SpringMVC…

Netty中使用编解码器框架

目录 什么是编解码器&#xff1f; 解码器 将字节解码为消息 将一种消息类型解码为另一种 TooLongFrameException 编码器 将消息编码为字节 将消息编码为消息 编解码器类 通过http协议实现SSL/TLS和Web服务 什么是编解码器&#xff1f; 每个网络应用程序都必须定义如何…

STM32学习笔记——定时器

目录 一、定时器功能概述 1、基本定时器&#xff08;TIM6&TIM7&#xff09; 工作原理 时序 2、通用计时器&#xff08;TIM2&TIM3&TIM4&TIM5&#xff09; 时钟源 外部时钟源模式1&2 外部时钟源模式2 外部时钟源模式1 定时器的主模式输出 输入捕获…

spring boot和spring cloud项目中配置文件application和bootstrap中的值与对应的配置类绑定处理

在前面的文章基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136065211 加载完文件转换为 Environment 中对应的值之后&#xff0c;接下来需要将对应的值与对应的配置类进行绑定&#xff0c;方便对应的组件取值处理接下来的操作。 对应的配置值与配置类绑定通过 Con…

百面嵌入式专栏(面试题)C语言面试题22道

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍C语言相关面试题 。 宏定义是在编译的哪个阶段被处理的?答案:宏定义是在编译预处理阶段被处理的。 解读:编译预处理:头文件包含、宏替换、条件编译、去除注释、添加行号。 写一个“标准”宏MIN,这个…

命令行参数、环境变量

1. 命令行参数 大家平时在写主函数时基本是无参的&#xff0c;但其实是有参数的&#xff0c;先介绍前两个参数。 int main(int argc, char* argv[])第二个参数是指针数组&#xff0c;第一个参数是该数组的个数&#xff0c;我们先来写 一段代码来看看指针数组里面是什么。 1 #…

第二讲:数据结构 AcWing 826. 单链表

目录 数组模拟链表数组模拟单链表 单链表思路 && 代码 看图更好理解推荐一下y总的刷题网站 数组模拟链表 笔试的题目大部分 大部分涉及到链表都是十万级别的 用数组的方式创建链表速度很快&#xff0c;不会超时&#xff0c;而如果用new 一个结构体的话 大部分就是比较…

Unity类银河恶魔城学习记录4-4 4-5 P57-58 On Hit Impactp- Attack‘direction fix源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Entity.cs using System.Collections; using System.Collections.Generic;…

CSS的2D变换

CSS的2D变换 1. 浏览器的二维坐标 如图所示 2. 2D位移 2D 位移&#xff1a;可以改变元素的位置&#xff0c;具体使用方式&#xff1a;给元素添加转换属性transform。 属性名作用translateXx轴位移translateYy轴位移translate一个值代表x方向&#xff0c;两个值代表&#xf…

新型RedAlert勒索病毒针对VMWare ESXi服务器

前言 RedAlert勒索病毒又称为N13V勒索病毒&#xff0c;是一款2022年新型的勒索病毒&#xff0c;最早于2022年7月被首次曝光&#xff0c;主要针对Windows和Linux VMWare ESXi服务器进行加密攻击&#xff0c;到目前为止该勒索病毒黑客组织在其暗网网站上公布了一名受害者&#x…

K8s环境下rook-v1.13.3部署Ceph-v18.2.1集群

文章目录 1.K8s环境搭建2.Ceph集群部署2.1 部署Rook Operator2.2 镜像准备2.3 配置节点角色2.4 部署operator2.5 部署Ceph集群2.6 强制删除命名空间2.7 验证集群 3.Ceph界面 1.K8s环境搭建 参考&#xff1a;CentOS7搭建k8s-v1.28.6集群详情&#xff0c;把K8s集群完成搭建&…

Codeforces Round 923 (Div. 3)

Codeforces Round 923 (Div. 3) Codeforces Round 923 (Div. 3) A. Make it White 题意&#xff1a;略 思路&#xff1a;找最小和最大的‘B’下标即可 AC code&#xff1a; void solve() {cin >>n;string s; cin>> s;int mn INF, mx 0;for (int i 0; i <…

优化Mac电脑文件管理工具cleanmymac2024

在日常的Mac使用过程中&#xff0c;有效的文件管理策略是保持设备高效运行的关键。随着时间的推移&#xff0c;无用的文件和忘记的数据可能会悄悄占据你的硬盘空间&#xff0c;导致设备变慢&#xff0c;甚至影响你的工作效率。因此&#xff0c;学习Mac文件管理&#xff0c;并定…

【操作系统】MacOS虚拟内存统计指标

目录 命令及其结果 参数解读 有趣的实验 在 macOS 系统中&#xff0c;虚拟内存统计指标提供了对系统内存使用情况和虚拟内存操作的重要洞察。通过分析这些指标&#xff0c;我们可以更好地了解系统的性能状况和内存管理情况。 命令及其结果 >>> vm_stat Mach Virtu…

JavaWeb后端——控制反转IOC/依赖注入DI

控制反转&#xff1a;why&#xff0c;目标是要做到控制反转 依赖注入&#xff1a;how&#xff0c;如何实现控制反转&#xff0c;控制反转有很多方法&#xff0c;依赖注入是其中一种方法 控制反转&#xff08;Inversion of Control, IoC&#xff09;和依赖注入&#xff08;Depe…

【每日一题】LeetCode——链表的中间结点

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有…

VRRP配置

目录 网络拓扑图 配置要求 配置步骤 网络拓扑图 配置要求 按照图示配置 IP 地址和网关在 SW1&#xff0c;SW2&#xff0c;SW3 上创建 Vlan10 和 Vlan20&#xff0c;对应 IP 网段如图&#xff0c;交换机之间链路允许所有 VLAN 通过在 SW1 和 SW2 上配置 VRRP&#xff0c;要求…

【Java】ArrayList和LinkedList的区别是什么

目录 1. 数据结构 2. 性能特点 3. 源码分析 4. 代码演示 5. 细节和使用场景 ArrayList 和 LinkedList 分别代表了两类不同的数据结构&#xff1a;动态数组和链表。它们都实现了 Java 的 List 接口&#xff0c;但是有着各自独特的特点和性能表现。 1. 数据结构 ArrayList…

C语言:函数递归

1. 递归是什么&#xff1f; 先来看最简单的递归代码&#xff1a; #include <stdio.h>int main() {printf("Hello World\n");main();return 0; } 在main函数里还有一个main函数&#xff0c;在XXX函数里有XXX函数&#xff0c;这种就是递归 在函数里调用自己&…
最新文章