C++数据结构:二叉树

news2025/1/19 15:01:05

二叉树

每个结点最多只有二棵子树,也就是二叉树中没有度大于2的结点。二叉树的子树有左右之分,严格区分左孩子、右孩子,其次序不能颠倒


二叉树五种基本形态

特殊二叉树

斜树

所有节点都只有左子树的二叉树叫做左斜树,所有节点都只有右子树的二叉树叫做右斜树。左斜树和右子树统称为斜树。

斜树已经退化成线性结构,二叉树在查找上表现出来优异性能在斜树得不到体现。

满二叉树

所有的节点都同时具有左子树和右子树。所有的叶子节点都在同一层上。

完全二叉树

完全二叉树从根结点到倒数第二层满足满二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。

二叉树性质

  1. 在二叉树的第i层上至多有2i-1(i ≥1)。
  2. 深度为k的二叉树至多有个结点(k≥1)。
  3. 具有n个结点的完全二叉树的深度为。
  4. 在任意一棵二叉树中,若终端结点的个数为,度为2的结点数为,则。
  5. 对于有n个结点的完全二叉树中编号为i的结点:
    • 若,则结点i是二叉树的根
    • 若,则其双亲节点为
    • 若,则结点i的左子节点为2i
    • 若,则结点i无左子结点
    • 若,则结点i的右子结点为
    • 若,则结点i无右子结点

二叉树的典型遍历方式

前序遍历

  1. 如果二叉树为空,则无操作,直接返回。
  2. 如果二叉树非空,则执行以下操作:
    A. 访问根结点;
    B. 先序遍历左子树
    C. 先序遍历右子树。

中序遍历

  1. 如果二叉树为空,则无操作,直接返回。
  2. 如果二叉树非空,则执行以下操作:
    A. 中序遍历左子树;
    B. 访问根结点;
    C. 中序遍历右子树。

后序遍历

  1. 如果二叉树为空,则无操作,直接返回。
  2. 如果二叉树非空,则执行以下操作:
    A. 后序遍历左子树;
    B. 后序遍历右子树;
    C. 访问根结点。

二叉树的存储

顺序存储结构

一维数组存储二叉树中的结点,必须把二叉树的所有结点安排成一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系。用编号的方法从树根起,自上层至下层,每层自左至右地给所有结点编号。这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。

满二叉树

对于一般的非完全二叉树来说,如果仍然按照从上到下、从左到右的次序存储在一维数组中,则数组下标之间不能准确反映树中结点间的逻辑关系,可以在非完全二叉树中添加一些并不存在的空结点使之变成完全二叉树

优点:读取某个指定的节点的时候效率比较高
缺点:有可能对存储空间造成极大的浪费,在最坏的情况下,一棵深度为k的右斜树,它只有k个结点,却需要个结点存储空间。这显然是对存储空间的严重浪费,所以顺序存储结构一般只用于完全二叉树或满二叉树。

链式存储结构

链表来表示一棵二叉树,每个结点最多有两个孩子,因此,每个结点除了存储自身的数据外,还应设置两个指针分别指向左、右孩子结点。当没有孩子结点时,相应的指针域置为空。

  • 优点:相对二叉树比较大的时候浪费空间较少
  • 缺点:有可读取某个指定节点的时候效率偏低。

Demo

MyTree.h

#pragma once
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
/********************************************
*******************顺序存储*******************
*********************************************/
template<typename T>
class CMyTree_Arr
{
	vector<T> vec_buff;
public:
	CMyTree_Arr();
	~CMyTree_Arr();
	void clera();
	void initTree(T arr[], size_t length);
	bool find(T const& findVal)const;
	void appendNode(T const& data);
	void prePrint(int index = 1);
	void inPrint(int index = 1);
	void posPrint(int index = 1);
private:
	int _find(T const& finaVal)const;
};

template <typename T>
CMyTree_Arr<T>::CMyTree_Arr()
{
	vec_buff.clear();
}

template <typename T>
CMyTree_Arr<T>::~CMyTree_Arr()
{
	vec_buff.clear();
}

template <typename T>
void CMyTree_Arr<T>::clera()
{
	vec_buff.clear();
}

template <typename T>
void CMyTree_Arr<T>::initTree(T arr[], size_t length)
{
	vec_buff.clear();
	vec_buff.push_back(-1);
	vec_buff.insert(vec_buff.end(), arr, arr + length);
	//vector<T> temp_vector(arr, arr + length);
	//vec_buff = temp_vector;
}

template <typename T>
bool CMyTree_Arr<T>::find(T const& findVal) const
{
	return _find(findVal) != -1;
}

template <typename T>
void CMyTree_Arr<T>::appendNode(T const& data)
{
	vec_buff.push_back(data);
}

template <typename T>
void CMyTree_Arr<T>::prePrint(int index)
{
	if (index < vec_buff.size() && index >= 0)
	{
		cout << vec_buff[index] << " ";//根
		prePrint(2 * index);//前序遍历左子树
		prePrint(2 * index + 1);//前序遍历右子树
	}
}

template <typename T>
void CMyTree_Arr<T>::inPrint(int index)
{
	if (index < vec_buff.size() && index >= 0)
	{
		inPrint(2 * index);//中序遍历左子树
		cout << vec_buff[index] << " ";//根
		inPrint(2 * index + 1);//中序遍历右子树
	}
}

template <typename T>
void CMyTree_Arr<T>::posPrint(int index)
{
	if (index < vec_buff.size() && index >= 0)
	{
		posPrint(2 * index);//后序遍历左子树
		posPrint(2 * index + 1);//后序遍历右子树
		cout << vec_buff[index] << " ";//根
	}
}

template <typename T>
int CMyTree_Arr<T>::_find(T const& finaVal) const
{
	auto iter = std::find(vec_buff.begin(), vec_buff.end(), finaVal);
	if (iter != vec_buff.end())
	{
		return &*iter - &vec_buff[0];//下标
	}
	return -1;
}
/********************************************
*******************链式存储*******************
*********************************************/
template<typename T>
//树结点
struct TreeNode
{
	T data;
	TreeNode *lChild, *rChild;//左、右子节点
	TreeNode() :lChild(nullptr), rChild(nullptr){}
};

template<typename T>
class CMyTree_Node
{
	TreeNode<T> *pRoot;//根节点
public:
	CMyTree_Node();
	~CMyTree_Node();
	void clear();
	TreeNode<T> * initTree(T arr[], size_t length, int index = 0);
	void prePrint();
	void inPrint();
	void posPrint();
	void levelPrint();
private:
	void clear(TreeNode<T>*& root);//借助递归删除所有子树
	void prePrint(TreeNode<T>*& root);
	void inPrint(TreeNode<T>*& root);
	void posPrint(TreeNode<T>*& root);
	void levelPrint(TreeNode<T>*& root);
};

template <typename T>
CMyTree_Node<T>::CMyTree_Node()
{
	pRoot = nullptr;
}

template <typename T>
CMyTree_Node<T>::~CMyTree_Node()
{
	clear();
}

template <typename T>
void CMyTree_Node<T>::clear()
{
	clear(pRoot);
}

template <typename T>
TreeNode<T> * CMyTree_Node<T>::initTree(T arr[], size_t length, int index)
{
	if (index >= length)
		return nullptr;
	//前序构造
	TreeNode<T>* node = new TreeNode<T>;
	node->data = arr[index];
	node->lChild = initTree(arr, length, 2 * index + 1);
	node->rChild = initTree(arr, length, 2 * index + 2);

	pRoot = node;
	return node;
}

template <typename T>
void CMyTree_Node<T>::prePrint()
{
	prePrint(pRoot);
}

template <typename T>
void CMyTree_Node<T>::inPrint()
{
	inPrint(pRoot);
}

template <typename T>
void CMyTree_Node<T>::posPrint()
{
	posPrint(pRoot);
}

template <typename T>
void CMyTree_Node<T>::levelPrint()
{
	levelPrint(pRoot);
}

template <typename T>
void CMyTree_Node<T>::prePrint(TreeNode<T>*& root)
{
	if (root)
	{
		cout << root->data << " ";
		prePrint(root->lChild);
		prePrint(root->rChild);
	}
}

template <typename T>
void CMyTree_Node<T>::inPrint(TreeNode<T>*& root)
{
	if (root)
	{
		inPrint(root->lChild);
		cout << root->data << " ";
		inPrint(root->rChild);
	}
}

template <typename T>
void CMyTree_Node<T>::posPrint(TreeNode<T>*& root)
{
	if (root)
	{
		posPrint(root->lChild);
		posPrint(root->rChild);
		cout << root->data << " ";
	}
}

template <typename T>
void CMyTree_Node<T>::levelPrint(TreeNode<T>*& root)
{
	queue<TreeNode<T>*> *que = new queue<TreeNode<T>*>;
	root = pRoot;
	que->push(pRoot);
	while (!que->empty())
	{
		root = que->front();
		que->pop();
		cout << root->data << " ";

		if (root->lChild)
			que->push(root->lChild);
		if (root->rChild)
			que->push(root->rChild);
	}
}

template <typename T>
void CMyTree_Node<T>::clear(TreeNode<T>*& root)
{
	if (root)
	{
		clear(root->lChild);
		clear(root->rChild);
		delete root;
		root = nullptr;
	}
}

cpp

// 05binarytree.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "MyTree.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	CMyTree_Arr<int>* mta = new CMyTree_Arr<int>;
	mta->initTree(arr, 10);
	bool b = mta->find(6);
	b = mta->find(11);
	mta->prePrint();
	cout << endl;
	mta->inPrint();
	cout << endl;
	mta->posPrint();
	mta->clera();
	delete mta;
	cout << endl;
	cout << "--------------------------" << endl;
	CMyTree_Node<int>* mtn = new CMyTree_Node<int>;
	mtn->initTree(arr, 10);
	mtn->prePrint();
	cout << endl;
	mtn->inPrint();
	cout << endl;
	mtn->posPrint();
	cout << endl;
	mtn->levelPrint();
	mtn->clear();
	delete mtn;
	return 0;
}

// 1 将CMyTree_Node中创建二叉树改成中序和后序
// 2 (1)不采用递归的方式来先序遍历,中序遍历 (2)判断一颗数是否是完全二叉树
//(1)和(2)二选一
//思路1 层序遍历二叉树,找到第一个非满结点 如何之后的结点还有非满结点,则不是
//思路2 讲所有结点全部压入队列,每次判断队列头为空则跳出循环 如果队列后面还有元素就不是完全二叉树

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

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

相关文章

SAP ABAP 创建后台定时任务job

定时任务的事务码 sm36:创建定时任务 sm37:查看定时任务 JDBG:后台任务debug,在对应的sm37中对应的job页面 t-code输入 创建定时任务SM36 1、名称可以随便起一般都是按自己公司业务情况来&#xff0c;比如我这个就是哪一个报表的定时任务&#xff0c;做的是什么操作&#xf…

Python 全栈系列220 Tornado的服务搭建

说明 想法变的真快 本来是没打算用Tornado的&#xff0c;主要是想节约时间。但是现在看来不用还是不行&#xff1a;目前用gevent flask部署的时候&#xff0c;启动多核的worker似乎存在问题。 另外&#xff0c;有很多内部基础的数据服务&#xff0c;其实并不需要flask的各种组…

如何使用镭速保护云存储数据安全

近年来&#xff0c;随着云计算的发展&#xff0c;远程系统上的数据存储变的越来越重要。云存储是一个以数据存储和管理为核心的云计算系统&#xff0c;给我们提供了一种全新的数据信息存储模式。但是&#xff0c;可以从全球任何地方访问和检索相同的数据。所需要的只是一个简单…

ZooKeeper分布式应用程序协调服务

目录 一.ZooKeeper基本介绍 1.ZooKeeper是什么&#xff1f; 2.ZooKeeper的工作机制 3.ZooKeeper的特点 4.ZooKeeper的数据结构 5.ZooKeeper的应用场景 5.1 统一命名服务 5.2 统一配置管理 5.3 统一集群管理 5.4 服务器动态上下线 5.5 软负载均衡 二.ZooKeeper的选举…

EasyConnect登陆报错:拉起虚拟网卡失败,请确保虚拟网卡已经安装在系统上并处于启用状态,然后再重新登录解决此问题。

目录 项目场景&#xff1a; 问题描述 原因分析&#xff1a; 解决方案&#xff1a; 1. 搜索设备管理器&#xff0c;打开/或者打开此电脑-&#xff08;顶部&#xff09;计算机-属性-设备管理器进入 2. 进入设备管理器&#xff0c;找到网络适配器&#xff0c;双击网络适配器…

【云原生】k8s Service 实现服务发现和负载均衡

文章目录 前言Service 介绍Service 的四种类型及使用方式Service 的定义和使用通过命令创建服务查看创建的服务情况 不指定 Selectors 的服务Headless 服务Service 工作原理及原理图Ingress 讲解集群外部如何访问服务总结 前言 在容器编排系统中&#xff0c;如 Kubernetes&…

Anaconda + TensorFlow Winodws环境安装(Windows Terminal / Visual Studio / VS code)

目录 前言个人环境 Anaconda安装下载安装测试添加到windows terminal TensorFlow环境配置安装测试 搭配Visual Studio 2022搭配VS Code 前言 以前发生的一些事情&#xff0c;让我认识到即便配环境这种事情&#xff0c;最好还是把自己的过程存个档 &#xff0c;这个的安装虽然简…

前端HTML、CSS--11(CSS-8)

目录&#xff1a; CSS3 属性选择器 CSS3 结构伪类选择器 CSS3 伪元素选择器 CSS3 2D转换 CSS3 动画 CSS3 3D转换 浏览器私有前缀 4. 2D转换 转换&#xff08;transform&#xff09;是CSS3中具有颠覆性的特性之一&#xff0c;可以实现元素的位移、旋转、缩放等效果。 转…

技术引领,创邻科技Galaxybase亮相2023数据技术嘉年华

2023年4月7日-8日&#xff0c;由中国DBA联盟&#xff08;ACDU&#xff09;和墨天轮社区联合主办的第十二届『数据技术嘉年华』&#xff08;DTC2023&#xff09;在北京举办。本次大会以“开放融合数智化——引领数据技术发展&#xff0c;释放数据要素价值”为主题&#xff0c;从…

线下活动 | 探索现代服务业的人效提升之路

3月25日&#xff0c;盖雅搞得定系列活动-卓越管理闭门分享会圆满落幕。此次活动主题定为「探索服务业的人效提升之路」&#xff0c;20余位现代服务业企业代表参与了此次活动&#xff0c;共同探讨如何利用数字化工具赋能企业管理&#xff0c;促进人效提升。招商积余人力负责人谢…

仿函数详解

目录 1、函数对象 2、谓词 2.1、一元谓词&#xff08;谓词有一个参数&#xff09; 2.1.1、普通函数提供策略 2.1.2、仿函数&#xff08;上面的函数调用&#xff09;提供策略 2.1.3、一元谓词全部代码 2.1、二元谓词&#xff08;谓词有两个参数&#xff09; ​编辑 3、内…

Ansys Zemax | 如何模拟 LED 及其它复杂光源

概述 在使用非序列时&#xff0c;对照明系统进行精确模拟的第一步总是要正确建立光源模型。OpticStudio 提供了多种精确模拟光源的方法。这篇文章介绍了如何在非序列模式下使用径向光源 (Source Radial), 光源文件 (Source File) 以及通过建立其他复杂几何体&#xff0c;来对le…

Oracle与Mysql求连续天数的数据

问题&#xff1a;最近有开发需求&#xff0c;查询最近一个月连续天数>2的数据项&#xff0c;我这里数据库日期存储的是yyyymmdd&#xff0c;字符串 (可自行根据自身情况修改) 思路&#xff1a;如果可以找到连续日期的开始与结束时间&#xff0c;那么就可以 where 日期 betw…

D. Shortest Cycle(floyd求最小环)

Problem - D - Codeforces 给你n个整数a1,a2,..., ana1,a2,...,an。考虑n个节点的图&#xff0c;其中节点ii, jj (i≠ji≠j)是相连的&#xff0c;当且仅当&#xff0c;aiaiAND aj≠0aj≠0&#xff0c;其中AND表示位数和操作。 请找出该图中最短周期的长度&#xff0c;或确定它…

数据库锁表原因、排查、解决

数据库锁表原因、排查、解决 一.场景场景1场景2 二.原因三.排查四.解决方案 一.场景 场景1 锁表通常发生在DML&#xff08; insert 、update 、delete &#xff09; A操作进行全量数据同步&#xff0c;对整个表的粒度进行上锁&#xff0c;导致B操作只能等待A操作完成才能进入…

Vue中mixins(混入)的介绍和使用

什么是Mixin&#xff1f; 想要使用一个事物或者工具&#xff0c;我们首要先了解它是什么&#xff0c;这样我们才好对症下药。 其实Mixin不是Vue专属的&#xff0c;可以说它是一种思想&#xff0c;也可以说它就是混入的意思&#xff0c;在很多开发框架中都实现了Mixin(混入)&a…

我的第一本书终于要印刷了!

终于要印刷了 编辑发来了一个好消息&#xff0c;我的书最快下周就可以印刷出版了。 从开始动笔到提交第一稿&#xff0c;前后有1年的时间&#xff0c; 紧接着和出版社胡老师一起修改&#xff0c; 从一审、二审、三审&#xff0c; 到一校、二校、三校&#xff0c; 确认书…

MobileNetV3详细原理(含torch源码)

作者&#xff1a;爱笑的男孩。 个人简介&#xff1a;打工人。 持续分享&#xff1a;机器学习、深度学习、python相关内容、日常BUG解决方法及Windows&Linux实践小技巧。 如发现文章有误&#xff0c;麻烦请指出&#xff0c;我会及时去纠正。有其他需要可以私信我或者发我邮箱…

企业对外投资为何一定要申请境外投资备案?

随着贸易经济全球化的发展&#xff0c;国内外企业也纷纷把目标转向海外市场&#xff0c;尤其香港&#xff0c;日本&#xff0c;东南亚等地的投资有增无减。 境外投资备案&#xff0c;就是在中国境内设立的公司主体对中国以外的国家进行投资&#xff0c;需要在商务部和发改委进…

高级数据结构与算法 | 自适应基数树(Adaptive Radix Tree)

文章目录 AdaptiveRadixTree基本介绍自适应节点内部节点叶子节点 高度压缩Path CompressionLazy Expansion 算法SearchInsertDeleteBulk loading 并发乐观锁耦合读优化写排除&#xff08;ROWEX&#xff09;适配 ROWEX节点替换路径压缩 AdaptiveRadixTree 基本介绍 论文链接&am…