树
定义与性质
- 树是指由 n(n≥0)个结点组成的有穷集合 D 与 D 上的关系的集合 R构成的结构,通常我们用 T 表示。
- 树需要满足以下三个条件:
- 当n=0时,该结点集合为空,该树被称为空树
- 在任意的非空树中,有且仅有一个根结点root
- 当 n>1 时 ,除根结点以外的其余结点可以分为 m(m>0)个不相交的子集D1,D2,D3,...,Dm, 其中每一个这样的子集 Di(i≤m)本身又是一棵树, 称为根结点 root的子树
- 树的几个重要术语:
- 结点的度:结点拥有的直接子树数目。
- 数的度:书中各结点度的最大值。
- 分支结点:也是非叶子结点,度不为0的结点。
- 叶子结点:也叫做终端结点,度为0的结点。
- 树的深度:树中结点的最大层次数。
- 森林:n(n≥0)棵不相交的树的集合称为森林。
- 常用性质:(针对二叉树)
- 具有n个结点的非空二叉树有且仅有n-1个分支。
- 非空二叉树第i层最多有 2 i − 1 2^{i-1} 2i−1个结点。
- 深度为k的二叉树最多有 2 k − 1 2^k-1 2k−1个结点。
- 存储方式:
- 顺序存储:顺序存储将二叉树的每一个节点和数组的编号一一对应,根据这个编号,可以找到该结点的父结点和孩子结点。
- 链式存储:二叉树的节点一般定义为:数据部分、左子树指针和右子树指针,用指针将节点串起来。
- 两种存储方式的优缺点对比:
- 顺序存储结构:
- 优点:访问速度快 ,可以通过下标索引直接访问目标元素,访问时间复杂度为
O(1)
。节省内存空间(适用于完全二叉树),对于完全二叉树,顺序存储可以非常高效且节省内存空间,树中每个结点在内存中都可以紧密排布 - 缺点:不适合非完全二叉树 ,非完全二叉树进行顺序存储可能会造成大量的空间浪费,因为数组中可能有大量的空位来表示不存在的节点。难以动态扩容 ,树的顺序存储需要固定大小的数组。插入和删除操作不方便 ,效率较低。如果树结点比较多,可能内存空间有限找不到一整片连续的内存来顺序存储树的结点。
- 优点:访问速度快 ,可以通过下标索引直接访问目标元素,访问时间复杂度为
- 链式存储结构:
- 优点:适合非完全二叉树,插入和删除操作方便,动态扩容方便。
- 缺点:访问速度较慢,空间开销大,因为链式存储还需要额外存储指针数据。
- 顺序存储结构:
- 完全二叉树:一种特殊的二叉树
- 除了最后一层外,其他每一层都要被节点完全填满。
- 最后一层的结点都尽可能的靠左排列。
- 完全二叉树适合用顺序存储结构来存储。
代码实现
以下代码实现都是基于二叉树的。
二叉树结点类
这里基于链式存储。
cpp
/**
* 二叉树结点类
*/
class BinaryTreeNode
{
private:
int *data;
public:
BinaryTreeNode *lchild;
BinaryTreeNode *rchild;
BinaryTreeNode()
{
data = NULL;
lchild = NULL;
rchild = NULL;
}
BinaryTreeNode(int n)
{
data = new int;
*data = n;
lchild = NULL;
rchild = NULL;
}
int getData()
{
return *data;
}
};
创建二叉树:
cpp
// 递归创建二叉树(层序)
BinaryTreeNode *createBinaryTree(vector<int> &nums, int index, int size)
{
if (index >= size)
{
return NULL;
}
BinaryTreeNode *node = new BinaryTreeNode(nums[index]);
node->lchild = createBinaryTree(nums, 2 * index + 1, size);
node->rchild = createBinaryTree(nums, 2 * index + 2, size);
return node;
}
四种遍历方法(前序、中序、后序、层序)
这里的前序、中序、后序遍历都是基于递归实现的,当然也有非递归的方法,可以参考:二叉树的前中后和层序遍历详细图解(递归和非递归写法)_二叉树前序列为abcdefg的图-CSDN博客
cpp
/**
* 前序遍历
*/
void preOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
cout << root->getData() << " ";
preOrder(root->lchild);
preOrder(root->rchild);
}
/**
* 中序遍历
*/
void inOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
inOrder(root->lchild);
cout << root->getData() << " ";
inOrder(root->rchild);
}
/**
* 后序遍历
*/
void postOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
postOrder(root->lchild);
postOrder(root->rchild);
cout << root->getData() << " ";
}
/**
* 层序遍历
*/
void levelOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
// 层序遍历借助队列实现
queue<BinaryTreeNode *> q;
BinaryTreeNode *front;
q.push(root);
while (!q.empty())
{
front = q.front();
q.pop();
if (front->lchild)
{
q.push(front->lchild);
}
if (front->rchild)
{
q.push(front->rchild);
}
cout << front->getData() << " ";
}
}
测试用例
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
/**
* 二叉树结点类
*/
class BinaryTreeNode
{
private:
int *data;
public:
BinaryTreeNode *lchild;
BinaryTreeNode *rchild;
BinaryTreeNode()
{
data = NULL;
lchild = NULL;
rchild = NULL;
}
BinaryTreeNode(int n)
{
data = new int;
*data = n;
lchild = NULL;
rchild = NULL;
}
int getData()
{
return *data;
}
};
// 递归创建二叉树(层序)
BinaryTreeNode *createBinaryTree(vector<int> &nums, int index, int size)
{
if (index >= size)
{
return NULL;
}
BinaryTreeNode *node = new BinaryTreeNode(nums[index]);
node->lchild = createBinaryTree(nums, 2 * index + 1, size);
node->rchild = createBinaryTree(nums, 2 * index + 2, size);
return node;
}
/**
* 前序遍历
*/
void preOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
cout << root->getData() << " ";
preOrder(root->lchild);
preOrder(root->rchild);
}
/**
* 中序遍历
*/
void inOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
inOrder(root->lchild);
cout << root->getData() << " ";
inOrder(root->rchild);
}
/**
* 后序遍历
*/
void postOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
postOrder(root->lchild);
postOrder(root->rchild);
cout << root->getData() << " ";
}
/**
* 层序遍历
*/
void levelOrder(BinaryTreeNode *root)
{
if (root == NULL)
{
return;
}
// 层序遍历借助队列实现
queue<BinaryTreeNode *> q;
BinaryTreeNode *front;
q.push(root);
while (!q.empty())
{
front = q.front();
q.pop();
if (front->lchild)
{
q.push(front->lchild);
}
if (front->rchild)
{
q.push(front->rchild);
}
cout << front->getData() << " ";
}
}
int main(int argc, char const *argv[])
{
vector<int> nums = {1, 2, 3, 4, 5, 6, 7}; // 完全二叉树的层次遍历序列
// 使用数组递归创建二叉树
BinaryTreeNode *root = createBinaryTree(nums, 0, nums.size());
cout << "前序遍历: ";
preOrder(root); // 前序遍历
cout << endl;
cout << "中序遍历: ";
inOrder(root); // 中序遍历
cout << endl;
cout << "后序遍历: ";
postOrder(root); // 后序遍历
cout << endl;
cout << "层序遍历: ";
levelOrder(root); // 层序遍历
cout << endl;
return 0;
}