树是一种递归定义的数据结构,如果树中节点的各子树从左到右是有次序的,不能互换,则称该树为有序树,否则叫无序树。

关于树的节点:

  1. 节点拥有的子树的个数叫做节点的度
  2. 如果度为0,那么该节点叫做叶节点或终端节点,除了根节点外的分支节点称为内部节点
  3. 树的度是各节点度的最大值。节点的子树的根称为该节点的子节点,某个节点只能有一个父节点。
  4. 节点的子树的根称为该节点的子节点
  5. 节点的层次从树根开始计算,树的深度或高度是树中节点的最大层数
  6. 相同父节点的孩子之间互称兄弟节点

二叉树的特点是每个节点最多有两棵子树,这意味着每个节点的度不会大于2,并且是一棵有序树

关于满二叉树:

  1. 所有分支节点都存在左子树和右子树
  2. 所有叶节点都在同一层
  3. 不存在非0或非2的节点
  4. 高度为H,含有(2^H-1)个节点

关于完全二叉树:

  1. 叶节点在最底下两层
  2. 最后一层叶节点都靠左侧排列,除了最后一层,其他层的节点个数达到最大
  3. 倒数第二层如果有叶节点,则叶节点都靠右排列
  4. 如果节点度为 1,则该节点只有左子树,不可以只有右子树。而且最多只有一个度为 1 的节点
  5. 高度为 H,每个节点都与高度为 H 的满二叉树中编号为 1~n 的节点一一对应

由此可见,满二叉树一定是完全二叉树,反之不一定成立

判断完全二叉树:

  1. 按照满二叉树的情形给二叉树的节点逐层编号,编号出现空缺则不是完全二叉树
  2. 一棵满二叉树,依次把编号最大的 1 到多个节点去掉,得到的就是一棵完全二叉树
  3. 如果一个完全二叉树有 n 个节点,当节点的编号≤(n/2) 时,这些节点就是分支节点,而当节点的编号>(n/2) 时,这些节点就是叶节点(n/2 如果没有整除则去掉小数部分)

二叉树的性质:

  1. 在二叉树的第i层上,最多有2^(i-1)个节点
  2. 高度为K的二叉树至多有(2^K-1)个节点
  3. 二叉树节点的总数量等于节点的总度数+1
  4. 对于任何一棵二叉树,如果其叶节点数量为n0,度为 2 的节点数量为n2,则叶节点的数量比有两棵子树的节点数量多一个,即n0=n2+ 1
  5. 具有 n(n>0)个节点的完全二叉树的高度为⌈log2(n+1)⌉ 或者 ⌊log2n⌋ +1。

顺序存储

顺序存储方式用一段连续的内存单元依次从上到下、从左到右存储二叉树各个节点元素

假设存储的是一棵完全二叉树,如果将根节点存储在数组下标i=1的位置,则左子树下标为i*2=2的位置,右子树在3的位置

如果存储的是一棵非完全二叉树,也按照完全二叉树的编号来存储,但会浪费较多的内存空间

#include <iostream>
#include <cmath> #define MaxSize 100 enum ECCHILDSIGN {
E_Root, // 树根
E_ChildLeft, // 左孩子
E_ChildRight // 右孩子
}; template <typename T>
struct BinaryTreeNode {
T data; // 数据域
bool isValid; // 节点是否有效
}; template <typename T>
class BinaryTree {
public:
BinaryTree() {
for (int i=0;i<=MaxSize;i++) {
// 初始时节点无效
SeqBiTree[i].isValid = false;
}
}
~BinaryTree() {}; public:
// 创建节点
int CreateNode(int parindex,ECCHILDSIGN pointSign,const T &e); // 获取父节点下标
int getParentIdx(int sonindex) {
if (ifValidRangeIdx(sonindex) == false) {
return -1;
}
if (SeqBiTree[sonindex].isValid == false) {
return -1;
}
return int(sonindex / 2);
} // 获取指定节点所在高度
int getPointLevel(int index) {
if (ifValidRangeIdx(index) == false) {
return -1;
}
if (SeqBiTree[index].isValid == false) {
return -1;
}
int level = int(log(index)/log(2)+1);
return level;
} // 获取二叉树深度
int getLevel() {
if (SeqBiTree[1].isValid == false) {
return 0;
} int i;
for (i = MaxSize;i >= 1;i--) {
// 找到最后一个有效节点
if (SeqBiTree[i].isValid == true) {
break;
}
}
return getPointLevel(i);
} // 判断是否为完全二叉树
bool ifCompleteBT() {
if (SeqBiTree[1].isValid == false) {
return false;
}
int i;
for (i = MaxSize;i >= 1;i--) {
// 找到最后一个节点
if (SeqBiTree[i].isValid == true) {
break;
}
}
for (int k = 1;k <= i;k++) {
// 所有节点有效
if (SeqBiTree[k].isValid == false) {
return false;
}
}
return true;
} void preOrder() {
if (SeqBiTree[1].isValid == false) {
return;
}
preOrder(1);
} void preOrder(int index) {
if (ifValidRangeIdx(index) == false) {
return;
}
if (SeqBiTree[index].isValid == false) {
return;
}
std::cout << (char)SeqBiTree[index].data << "";
preOrder(2 * index);
preOrder(2 * index + 1);
} private:
bool ifValidRangeIdx(int index) {
if (index < 1 || index > MaxSize) {
return false;
}
return true;
}
private:
BinaryTreeNode<T> SeqBiTree[MaxSize + 1];
}; template <class T>
int BinaryTree<T>::CreateNode(int parindex,ECCHILDSIGN pointSign,const T &e) {
if (pointSign != E_Root) {
if (ifValidRangeIdx(parindex) == false) {
return -1;
}
if (SeqBiTree[parindex].isValid == false) {
return -1;
}
} int index = -1;
if (pointSign == E_Root) {
index = 1; // 根节点下标为1
} else if (pointSign == E_ChildLeft) {
// 左孩子
index = 2 * parindex;
if (ifValidRangeIdx(index) == false) {
return -1;
}
} else {
// 右孩子
index = 2 * parindex + 1;
if (ifValidRangeIdx(index) == false) {
return -1;
}
}
SeqBiTree[index].data = e;
// 标记该下标中有效数据
SeqBiTree[index].isValid = true;
return index;
} int main(void) {
BinaryTree<int> tree;
int indexRoot = tree.CreateNode(-1,E_Root,'A');
int indexNodeB = tree.CreateNode(indexRoot,E_ChildLeft,'B');
int indexNodeC = tree.CreateNode(indexRoot,E_ChildRight,'C'); int indexNodeD = tree.CreateNode(indexNodeB,E_ChildLeft,'D');
int indexNodeE = tree.CreateNode(indexNodeC,E_ChildRight,'E'); int iParentIndexE = tree.getParentIdx(indexNodeE);
std::cout << "node E parent node index: " << iParentIndexE << std::endl; int iLevel = tree.getPointLevel(indexNodeD);
std::cout << "the height of node D: " << iLevel << std::endl; iLevel = tree.getPointLevel(indexNodeE);
std::cout << "the height of node E: " << iLevel << std::endl;
std::cout << "the depth of binary tree: " << tree.getLevel() << std::endl;
std::cout << "compelete binary tree: " << tree.ifCompleteBT() << std::endl; std::cout << "-----------------" << std::endl;
std::cout << "preorder: ";
tree.preOrder(); return 0;
}

链式存储

链式存储要存储额外的左右子节点,多用于存储普通的二叉树

#include <iostream>

enum ECCHILDSIGN {
E_Root, // 树根
E_ChildLeft, // 左孩子
E_ChildRight // 右孩子
}; template <typename T>
struct BinaryTreeNode {
T data; // 数据域
BinaryTreeNode *leftChild;
BinaryTreeNode *rightChild;
}; template <typename T>
class BinaryTree {
public:
BinaryTree();
~BinaryTree();
public:
// 创建节点
BinaryTreeNode<T> *CreateNode(BinaryTreeNode<T> *parentnode,ECCHILDSIGN pointSign,const T &e);
void ReleaseNode(BinaryTreeNode<T> *pnode); // 释放树节点
void CreateBTreeAccordPT(char *pstr); // 前序遍历顺序创建BTree
public:
// 前序遍历
void preOrder() {
preOrder(root);
}
void preOrder(BinaryTreeNode<T> *tNode) {
if (tNode != nullptr) {
std::cout << (char)tNode->data << " ";
preOrder(tNode->leftChild);
preOrder(tNode->rightChild);
}
} // 中序遍历
void inOrder() {
inOrder(root);
}
void inOrder(BinaryTreeNode<T> *tNode) {
if (tNode != nullptr) {
inOrder(tNode->leftChild);
std::cout << (char)tNode->data << " ";
inOrder(tNode->rightChild);
}
} // 后序遍历
void postOrder() {
postOrder(root);
}
void postOrder(BinaryTreeNode<T> *tNode) {
if (tNode != nullptr) {
postOrder(tNode->leftChild);
postOrder(tNode->rightChild);
std::cout << (char)tNode->data << " ";
}
} private:
BinaryTreeNode<T> *root;
void CreateBTreeAccordPTRecu(BinaryTreeNode<T>* &tnode,char* &pstr);
}; template <class T>
BinaryTree<T>::BinaryTree() {
root = nullptr;
} template <class T>
BinaryTree<T>::~BinaryTree() {
ReleaseNode(root);
} template <class T>
void BinaryTree<T>::ReleaseNode(BinaryTreeNode<T> *pnode) {
if (pnode != nullptr) {
ReleaseNode(pnode->leftChild);
ReleaseNode(pnode->rightChild);
}
delete pnode;
} template <class T>
BinaryTreeNode<T> *BinaryTree<T>::CreateNode(BinaryTreeNode<T> *parentnode,ECCHILDSIGN pointSign,const T &e) {
BinaryTreeNode<T> *tmpnode = new BinaryTreeNode<T>;
tmpnode->data = e;
tmpnode->leftChild = nullptr;
tmpnode->rightChild = nullptr; if (pointSign == E_Root) {
root = tmpnode;
}
if (pointSign == E_ChildLeft) {
parentnode->leftChild = tmpnode;
} else if (pointSign == E_ChildRight) {
parentnode->rightChild = tmpnode;
}
return tmpnode;
} template <class T>
void BinaryTree<T>::CreateBTreeAccordPT(char *pstr) {
CreateBTreeAccordPTRecu(root,pstr);
} template <class T>
void BinaryTree<T>::CreateBTreeAccordPTRecu(BinaryTreeNode<T> *&tnode,char *&pstr) {
if (*pstr == '#') {
tnode = nullptr;
} else {
tnode = new BinaryTreeNode<T>;
tnode->data = *pstr;
CreateBTreeAccordPTRecu(tnode->leftChild,++pstr);
CreateBTreeAccordPTRecu(tnode->rightChild,++pstr);
}
} int main(void) {
BinaryTree<int> tree;
BinaryTreeNode<int> *rootpoint = tree.CreateNode(nullptr,E_Root,'A');
BinaryTreeNode<int> *subpoint = tree.CreateNode(rootpoint,E_ChildLeft,'B');
subpoint = tree.CreateNode(subpoint,E_ChildLeft,'D');
subpoint = tree.CreateNode(rootpoint,E_ChildRight,'C');
subpoint = tree.CreateNode(subpoint,E_ChildRight,'E'); // tree.CreateBTreeAccordPT((char*)"ABD###C#E##"); // 直接通过前序遍历创建二叉树 std::cout << "preorder: ";
tree.preOrder();
std::cout << std::endl; std::cout << "inorder: ";
tree.inOrder();
std::cout << std::endl; std::cout << "postorder: ";
tree.postOrder();
std::cout << std::endl; return 0;
}

C++数据结构(树)的更多相关文章

  1. ACM数据结构-树状数组

    模板: int n; int tree[LEN]; int lowbit(int x){ return x&-x; } void update(int i,int d){//index,del ...

  2. Python入门篇-数据结构树(tree)的遍历

    Python入门篇-数据结构树(tree)的遍历 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.遍历 迭代所有元素一遍. 二.树的遍历 对树中所有元素不重复地访问一遍,也称作扫 ...

  3. Python入门篇-数据结构树(tree)篇

    Python入门篇-数据结构树(tree)篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.树概述 1>.树的概念 非线性结构,每个元素可以有多个前躯和后继 树是n(n& ...

  4. 常见基本数据结构——树,二叉树,二叉查找树,AVL树

    常见数据结构——树 处理大量的数据时,链表的线性时间太慢了,不宜使用.在树的数据结构中,其大部分的运行时间平均为O(logN).并且通过对树结构的修改,我们能够保证它的最坏情形下上述的时间界. 树的定 ...

  5. 数据结构和算法(Golang实现)(17)常见数据结构-树

    树 树是一种比较高级的基础数据结构,由n个有限节点组成的具有层次关系的集合. 树的定义: 有节点间的层次关系,分为父节点和子节点. 有唯一一个根节点,该根节点没有父节点. 除了根节点,每个节点有且只有 ...

  6. 数据结构--树(遍历,红黑,B树)

    平时接触树还比较少,写一篇博文来积累一下树的相关知识. 很早之前在数据结构里面学的树的遍历. 前序遍历:根节点->左子树->右子树 中序遍历:左子树->根节点->右子树 后序遍 ...

  7. 【poj 3167】Cow Patterns(字符串--KMP匹配+数据结构--树状数组)

    题意:给2个数字序列 a 和 b ,问按从小到达排序后,a中的哪些子串与b的名次匹配. a 的长度 N≤100,000,b的长度 M≤25,000,数字的大小 K≤25. 解法:[思考]1.X 暴力. ...

  8. JavaScript数据结构——树

    树:非顺序数据结构,对于存储需要快速查找的数据非常有用. 二叉树:二叉树中的节点最多只能有两个子节点(左侧子节点和右侧子节点).这些定义有助于我们写出更高效的向/从树中插入.查找和删除节点的算法. 二 ...

  9. 图解数据结构树之AVL树

    AVL树(平衡二叉树): AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.在AVL树中任何节点的两个子 ...

  10. Python 数据结构 树

    什么是树 数是一种抽象的数据类型(ADT)或是作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合,它是由n(n>1)的有限个节点和节点之间的边组成的一个有层次关系的集合. 树的组成 ...

随机推荐

  1. MybatisPlus的一些高级特性

    1.MybatisPlus多数据源配合 导入相关依赖 <dependency> <groupId>com.baomidou</groupId> <artifa ...

  2. 神经网络优化篇:机器学习基础(Basic Recipe for Machine Learning)

    机器学习基础 下图就是在训练神经网络用到的基本方法:(尝试这些方法,可能有用,可能没用) 这是在训练神经网络时用到地基本方法,初始模型训练完成后,首先要知道算法的偏差高不高,如果偏差较高,试着评估训练 ...

  3. gho、wim、esd的区别

    本文介绍的仅仅是压缩格式上的区别,并不是一些个体错误理解的 "GHOST 镜像 = 带驱动的快速装机镜像",实际 ESD 格式和 WIM 格式,也一样是可以集成驱动做成二次封装的快 ...

  4. 98秒转录2.5小时音频,最强音频翻译神器IFW下载部署

    IFW是一款功能强大的音频翻译工具,具备高速转录能力,能在短时间内完成大量音频处理,提高工作效率 以下是IFW在 Nvidia A100 - 80GB 上运行的一些基准测试: 最新中文版:https: ...

  5. Java的特性、内容和环境的配置

    Java的特性和优势 简单性 面向对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性 JDK包含JRE包含JVM JDK:Java Development Kit JRE:Java Run ...

  6. 年底了,网站被挂马了,关于IIS被陌生DLL劫持(新人发帖,写的不好的地方,请多多担待)

    一上班被分到两个需要杀毒的站点,情况是SEO被劫持 出现一些博彩信息,但是打开确实正常内容,使用站长工具的网站被黑检测功能,发现网站的HEAD前面加载一对加密的东西 一开始我使用D盾扫描网站,删除了一 ...

  7. 9.mysql的数据迁移到es中

    背景 从开发的角度说,就是老板叫我用es了,没那么多为什么,爸爸说了算 从业务角度,mysql已经不能满足我对全文检索的需求了.我需要检索某一个字段包含"圣诞节刚刚过去"这一字符串 ...

  8. IPv6实现内网穿透,极低成本保姆级教程

    摘要 一直想实现内网穿透从而达到随时随地可以连接到自己电脑的目的.尝试过使用一些付费的现成方案,但是价格偏高,而流量少得可怜,只能开放几个固定端口. 实现内网穿透的最大难点就在于拥有一个公网IP,但是 ...

  9. C++篇:第十二章_文件及IO_知识点大全

    C++篇为本人学C++时所做笔记(特别是疑难杂点),全是硬货,虽然看着枯燥但会让你收益颇丰,可用作学习C++的一大利器 十二.文件及IO 当在输入输出流中使用控制符进行格式控制时,需在程序中加入头文件 ...

  10. MindSpore实践:对篮球运动员目标的检测

    摘要:本文讲述的是MindSpore对篮球运动员目标的检测应用,通过AI技术辅助对篮球赛场进行分析. 本文分享自华为云社区<MindSpore大V博文系列:AI对篮球运动员目标的检测>,原 ...