二叉树和其他树
11.1树
- 定义
- 术语:
- 根(root):层次中最高层的元素
- 根的孩子(children):跟的下一级的元素
- 是余下所有元素所构成的子树的根
- 叶子(leaves):树中没有孩子的元素
- 级(level)/层次:一个元素的级=其父母的级+1。树根的级为1.
- 元素的度
- 指其孩子的个数
- 叶子节点 度=0
- 树的度:元素度的最大值
11.2 二叉树
一般,二叉树可以为空,树不能为空
- 表达式树:
11.3 二叉树的特性
- 包含n(n>0)个元素的二叉树的边数为 n − 1 n-1 n−1
- 二叉树高度为h且 h > = 0 h>=0 h>=0,则该二叉树最少有 h h h个元素,最多有 2 h − 1 2^h-1 2h−1个元素
- 包含n(n>0)个元素的二叉树,高度最大为n,最小为 [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]
满二叉树
- 当高度为h的二叉树恰好有
2
h
−
1
2^h-1
2h−1个元素时,称为满二叉树
完全二叉树(是满二叉树的一个特例)
- 当且仅当二叉树与k层满二叉树前1~n个节点所构成的二叉树结构相同时,深度为k具有n个节点的二叉树是一颗完全二叉树
- k层完全二叉树:
- 前k-1层为满二叉树
- 第k层上的节点都连续排列在第k层的左端
- 有n个元素的完全二叉树的深度为: [ l o g 2 ( n + 1 ) ] [log_2(n+1)] [log2(n+1)]
- 完全二叉树中一元素序号为i,1<=i<=n,则:
i=1
,该元素为二叉树的根;若i>1
,则该元素父节点的编号为 [ ( i / 2 ) ] [(i/2)] [(i/2)]2i>n
,该元素无左孩子;否则,左孩子编号为 2 i 2i 2i2i+1>n
,该元素无右孩子;否则,右孩子编号为 2 i + 1 2i+1 2i+1
11.4二叉树描述
11.4.1数组描述
- 二叉树元素按照其编号存储在数组的相应位置,可以看作是缺少了部分元素的完全二叉树
- 一个有n个元素的二叉树需要存储空间: n + 1 n+1 n+1~ 2 n 2^n 2n (或n~ 2 n − 1 2^n-1 2n−1 )
- 右斜二叉树存储空间最大
11.4.2链表描述
链表二叉树的节点结构
template <class T>
struct binaryTreeNode
{
T element;
binaryTreeNode<T> *leftChild, // 指向左孩子节点的指针
*rightChild; // 指向右孩子节点的指针
// 3个构造函数
binaryTreeNode() // 没有参数
{
leftChild = rightChild = nullptr;
}
binaryTreeNode(const T &theElement) // 只有数据参数
{
element(theElement);
leftChild = rightChild = nullptr;
}
binaryTreeNode(const T &theElement, // 数据 + 指针参数
binaryTreeNode *theLeftChild,
binaryTreeNode *theRightChild)
{
element(theElement);
leftChild = theLeftChild;
rightChild = theRightChild;
}
};
10.5二叉树常用操作
10.6二叉树遍历
- 许多二叉树操作是通过对二叉树进行遍历来完成
- 在二叉树的遍历中,每个元素都被访问到且仅被访问一次
- 在访问时执行对该元素的相应操作(复制、删除、输出等)
- 遍历方法:
前序遍历
template <class E>
void preOrder(binaryTreeNode<E> *t)
{ // 前序遍历二叉树 *t
if (t != nullptr)
{
visit(t); // 访问根节点
preOrder(t->leftChild); // 前序遍历左子树
preOrder(t->rightChild); // 前序遍历右子树
}
}
中序遍历
template <class E>
void inOrder(binaryTreeNode<E> *t)
{ // 中序遍历二叉树*t
if (t != nullptr)
{
inOrder(t->leftChild); // 中序遍历左子树
visit(t); // 访问根节点
inOrder(t->rightChild); // 中序遍历右子树
}
}
- 输出的表达式具有歧义性
- 前缀表达式:操作符位于操作数之前,操作数从左到右顺序出现
- 后缀表达式:操作符跟在操作数之后,操作数从左到右顺序出现
- 使用括号来避免歧义
- 每个操作符用括号括起来
- 每个操作数和操作符使用括号括起来
- 输出完全括号化的中缀表达式
template <class E>
void infix(binaryTreeNode<E> *t)
{ // 输出表达式的中缀形式
if (t != nullptr)
{
cout << '(';
infix(t->leftChild); // 左操作数
cout << t->element; // 操作符
infix(t->rightChild); // 右操作数
cout << ')';
}
}
后序遍历
template <class E>
void postOrder(binaryTreeNode<E> *t)
{ // 后序遍历二叉树*t
if (t != nullptr)
{
postOrder(t->leftChild); // 后序遍历左子树
postOrder(t->rightChild); // 后序遍历右子树
visit(t); // 访问根节点
}
}
层次遍历
template <class E>
void levelOrder(binaryTreeNode<E> *t)
{ // 层次遍历二叉树*t
arrayQueue<binaryTreeNode<E> *> q;
while (t != NULL)
{
visit(t); // 访问t
// 将t的左右孩子放入队列
if (t->leftChild)
q.push(t->leftChild);
if (t->rightChild)
q.push(t->rightChild);
try
{
t = q.front(); // 访问下一个节点
}
catch (queueEmpty)
{
return;
}
q.pop();
}
}
例题:
遍历算法性能
11.7抽象数据类型binaryTree
抽象数据类型 binaryTree {
实例:
元素集合;如果不空,则被划分为根、左子树和右子树;每个子树仍是一个二叉树
操作:
empty(): 如果二叉树为空,则返回true,否则返回false
size(): 返回二叉树的节点/元素个数
preorder(visit): 前序遍历二叉树,visit是访问函数
inOrder(visit): 中序遍历二叉树
postOrder(visit): 后序遍历二叉树
levelOrder(visit): 层次遍历二叉树
}
void( * )(T * )
是一种函数类型,返回值类型为void,参数类型为T*
template <class T>
class binaryTree
{
public:
virtual ~binaryTree() {}
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual void preOrder(void (*visit)(T *)) = 0; // 参数为函数指针
virtual void inOrder(void (*)(T *)) = 0;
virtual void postOrder(void (*)(T *)) = 0;
virtual void levelOrder(void (*)(T *)) = 0;
};
11.8类linkedBinaryTree
template <class E>
class linkedBinaryTree : public binaryTree<binaryTreeNode<E>>
{
private:
binaryTreeNode<E> *root; // 指向根的指针
int treeSize;//数的节点个数
static void (*visit)(binaryTreeNode<E> *); // 指针变量,指向一个函数
static void preOrder(binaryTreeNode<E> *t); // static代表类的静态函数
static void inOrder(binaryTreeNode<E> *t); // 不需实例化即可调用
static void postOrder(binaryTreeNode<E> *t); // 不能使用对象变量
static void dispose(binaryTreeNode<E> *t) { delete t; } // 删除t指向的节点
public:
linkedBinaryTree()
{
root = nullptr;
treeSize = 0;
}
~linkedBinaryTree() { erase(); }
bool empty() const { return treeSize == 0; }
// 设置好theVisit函数,前序遍历所有节点,并对每个节点调用theVisit函数
void preOrder(void (*theVisit)(binaryTreeNode<E> *))
{
visit = theVisit;
preOrder(root);
}
void inOrder(void (*theVisit)(binaryTreeNode<E> *))
{
visit = theVisit;
inOrder(root);
}
void postOrder(void (*theVisit)(binaryTreeNode<E> *))
{
visit = theVisit;
postOrder(root);
}
void levelOrder(void (*)(binaryTreeNode<E> *));
void erase()
{ // 使用后序遍历删除所有节点
postOrder(dispose);
root = nullptr;
treeSize = 0;
}
};
私有前序遍历函数
template <class E>
void linkedBinaryTree<E>::preOrder(binaryTreeNode<E> *t)
{
if (t != nullptr)
{
linkedBinaryTree<E>::visit(t); // 访问根节点,调用静态函数指针指向的函数
preOrder(t->leftChild); // 前序遍历左子树
preOrder(t->rightChild); // 前序遍历右子树
}
}
输出
template <class E>
class linkedBinaryTree : public binaryTree<binaryTreeNode<E>>
{
private:
…… static void output(binaryTreeNode<E> *t) { cout << t->element << ‘ ‘; }
public:
…… void preOrderOutput()
{
preOrder(output);
cout << endl;
}
void inOrderOutput()
{
inOrder(output);
cout << endl;
}
void postOrderOutput()
{
postOrder(output);
cout << endl;
}
void levelOutput()
{
levelOrder(output);
cout << endl;
}
};
计算高度
template <class E>
class linkedBinaryTree : public binaryTree<binaryTreeNode<E>>
{
private:
…… int height(binaryTreeNode<E> *t) const;
public:
…… int height() const { return height(root); }
};
template <class E>
int linkedBinaryTree<E>::height(binaryTreeNode<E> *t) const
{
if (t == nullptr) // 叶子节点再下一层
return 0;
int hl = height(t->leftChild); // 左子树的高度
int hr = height(t->rightChild); // 右子树的高度
if (hl > hr)
return ++hl;
else
return ++hr;
}
11.9应用
11.9.1设置信号放大器
- 求解策略
放置放大器和计算
degradeToLeaf
的伪代码
degradeToLeaf(i) = 0;
for (j in children(i))
{
if (degradeToLeaf(j) + degradeFromParent(j) > 容忍值)
{
在 j 放置放大器;
degradeToLeaf(i) = max(degradeToLeaf(i), degradeFromParent(j));
}
else
{
degradeToLeaf(i) = max(degradeToLeaf(i), degradeToLeaf(j) +
degradeFromParent(j));
}
}
-
树的二叉树描述
-
森林的二叉树表示
-
二叉树转换为树
-
二叉树转换为森林
并查集
void initialize(int numberOfElements)
{ // 初始化,每个元素是一个单独的树,的父节点为空
parent = new int[numberOfElements + 1];
for (int e = 1; e <= numberOfElements; e++)
parent[e] = 0;
}
int find(int theElement)
{ // 返回包含theElement的树的根节点
while (parent[theElement] != 0)
theElement = parent[theElement]; // 上移一层
return theElement;
}
void unite(int rootA, int rootB)
{ // 将根为rootA 和rootB的两棵树进行合并
parent[rootB] = rootA;
}
- 时间复杂性
- 性能改进