二叉树
1. 二叉树的定义
树:树是n(n≥0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:①有且仅有一个特定的称为根的结点;②当n>1时,其余结点可分为m个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树。
二叉树:二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。如图:
2. 二叉树的特点
(1)每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是只有两棵子树,而是最多有。没有子树或者有一颗子树都是可以的;
(2)左子树和右子树是有顺序的,次序不能任意颠倒;
(3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树,左子树和右子树如下图:
左边是左子树,右边是右子树。
3. 二叉树有五种基本形态
(1)空二叉树,就是什么结点都没有,包括根结点
(2)只有一个根结点
(3)根结点只有左子树
(4)根结点只有右子树
(5)根结点既有左子树又有右子树
4. 特殊二叉树(斜树,满二叉树,完全二叉树)
(1)斜树
字面意思,斜树就一定要是斜的,但是不是随便斜的,还是要有方向的。即:所有的结点都只有左子树的二叉树叫左斜树,所有的结点都只有右子树的二叉树叫右斜树,这两者统称为斜树。
(2)满二叉树
**在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的树称为满二叉树,**如图:
(3)完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同伴深度的满二叉树中编号为i的结点在二叉树中位置完全相同,那么这棵二叉树称为完全二叉树,如图:
注意:满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
非完全二叉树
如果上面5这个结点只有右子树11,没有左子树10,这就不是完全二叉树,如
注意:上面这两棵树都是非完全二叉树,也就是说在按照层次编号时,跳过了某一个结点。
5. 二叉树的存储结构
5.1 二叉树的顺序存储结构
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置也就是数组的下标要能体现结点之间的逻辑关系,如图
将这个二叉树存放在数组中,相应的下标对应其同样的位置,如
5.2 二叉链表
顺序存储适用性不强,因此考虑链式存储结构。二叉树每个结点最多有两个孩子,所以设计一个数据域和两个指针域是比较自然的想法,这样的链表是二叉链表。也就是说,申请两个指针域,一个指针域指向二叉树的左子树,一个指针域指向二叉树的右子树,数据域就指向二叉树所存储的数据。
6. 二叉树的遍历
二叉树的遍历是指从根结点出发,按照某种次序一次访问二叉树中的所有结点,使得每一个结点被访问一次且仅被访问一次。
6.1 二叉树的遍历方法1——前序遍历
规则是如果二叉树为空,则返回空,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树,即总结为:根左右
对于这个二叉树,访问顺序是,先从根结点A走下来,然后走左子树,A→B,以B为根结点,继续走其左子树,B→D,以D为根结点,走D的左子树为G,G为根结点,没有了,再走D的右子树H,这样以A为根结点的左子树已经走完;接下来走以A为根结点的右子树,走到C,以C为根结点,先走C的左子树,C→E,E为根结点没有左子树,但是有右子树,因此走I,走完后再走以C为根结点的右子树,因此最后的遍历顺序为:ABDEHCFIG
分析做法:将根结点下面的左右子树,那个为空就标记为“ * ”,这样的话上幅图就变为:
这样的话,遍历顺序为:ABDEHCFIG
代码为:
class Node// struct Node——public
{
public:
Node( ) :m_left(nullptr), m_right(nullptr) { }
Node(char v) :m_value(v), m_left(nullptr), m_right(nullptr) { }
char m_value;
Node* m_left;
Node* m_right;
};
class BTree //
{
private:
int m_flag;//表示 *
public:
Node* m_root;
public:
BTree() :m_root(nullptr), m_flag('*') { }
Node* Create(const char*& str);
void PreOrder(Node* t);//先序遍历
};
void BTree::PreOrder(Node* t)
{
if (t != nullptr)//根左右
{
cout << t->m_value << " ";//先遍历根
PreOrder(t->m_left);//遍历当前根t的左子树
PreOrder(t->m_right);//遍历当前根t的右子树
}
}
Node* BTree::Create(const char*& str)
{
if (*str == m_flag)
{
return nullptr;
}
else
{
//根据先序遍历,先创建根
Node* node = new Node(*str);//创建一个新结点
//创建左子树
node->m_left = Create(++str);
//创建右子树
node->m_right= Create(++str);
return node;
}
}
int main()
{
BTree bt;//定义了一棵树,树根为nullptr——m_root(nullptr)
const char * str = "ABD**EH***CF*I**G**";
bt.m_root = bt.Create(str);
cout << " PreOrder ";
bt.PreOrder(bt.m_root);
return 0;
}
运行结果:
6.2 二叉树的遍历方法2——中序遍历
规则为如果树为空,则返回空,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后返回访问根结点,嘴周中序遍历右子树,即总结为左根右。
对于这个二叉树,类似前序遍历,中序遍历的结果为:DBHEAFICG
做法同前序遍历,代码为:
class Node// struct Node——public
{
public:
Node( ) :m_left(nullptr), m_right(nullptr) { }
Node(char v) :m_value(v), m_left(nullptr), m_right(nullptr) { }
char m_value;
Node* m_left;
Node* m_right;
};
class BTree //
{
private:
int m_flag;//表示 *
public:
Node* m_root;
public:
BTree() :m_root(nullptr), m_flag('*') { }
Node* Create(const char*& str);
void InOrder(Node* t);//中序遍历
};
void BTree::InOrder(Node* t)
{
if (t != nullptr)//思想:左根右
{
InOrder(t->m_left);//先遍历当前根t的左子树
cout << t->m_value << " ";//遍历根
InOrder(t->m_right);//遍历当前根t的右子树
}
}
Node* BTree::Create(const char*& str)
{
if (*str == m_flag)
{
return nullptr;
}
else
{
//根据先序遍历,先创建根
Node* node = new Node(*str);//创建一个新结点
//创建左子树
node->m_left = Create(++str);
//创建右子树
node->m_right= Create(++str);
return node;
}
}
int main()
{
BTree bt;//定义了一棵树,树根为nullptr——m_root(nullptr)
const char * str = "ABD**EH***CF*I**G**";
bt.m_root = bt.Create(str);
cout << endl;
cout << " InOrder ";
bt.InOrder(bt.m_root);
return 0;
}
运行结果为:
6.3 二叉树的遍历方法3——后序遍历
规则为如果树为空,则返回空,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点,即总结为:左右根
还是这个二叉树,其按照后序遍历的结果为:DHEBIFGCA
代码为:
class Node// struct Node——public
{
public:
Node( ) :m_left(nullptr), m_right(nullptr) { }
Node(char v) :m_value(v), m_left(nullptr), m_right(nullptr) { }
char m_value;
Node* m_left;
Node* m_right;
};
class BTree //
{
private:
int m_flag;//表示 *
public:
Node* m_root;
public:
BTree() :m_root(nullptr), m_flag('*') { }
Node* Create(const char*& str);
void PreOrder(Node* t);//先序遍历
void InOrder(Node* t);//中序遍历
void PostOrder(Node* t);//后序遍历
int Size(Node* t);
int Height(Node* t);
};
void BTree::PostOrder(Node* t)
{
if (t != nullptr)//思想:左右根
{
PostOrder(t->m_left);//先遍历当前根t的左子树
PostOrder(t->m_right);//遍历当前根t的右子树
cout << t->m_value << " ";//遍历根
}
}
Node* BTree::Create(const char*& str)
{
if (*str == m_flag)
{
return nullptr;
}
else
{
//根据先序遍历,先创建根
Node* node = new Node(*str);//创建一个新结点
//创建左子树
node->m_left = Create(++str);
//创建右子树
node->m_right= Create(++str);
return node;
}
}
int main()
{
BTree bt;//定义了一棵树,树根为nullptr——m_root(nullptr)
const char * str = "ABD**EH***CF*I**G**";
bt.m_root = bt.Create(str);
cout << " PostOrder ";
bt.PostOrder(bt.m_root);
return 0;
}
运行结果:
6.4 计算二叉树的结点的个数和高度
二叉树结点的个数=左子树结点的个数+右子树结点的个数
高度类似,因此,代码为:
class Node// struct Node——public
{
public:
Node( ) :m_left(nullptr), m_right(nullptr) { }
Node(char v) :m_value(v), m_left(nullptr), m_right(nullptr) { }
char m_value;
Node* m_left;
Node* m_right;
};
class BTree //
{
private:
int m_flag;//表示 *
public:
Node* m_root;
public:
BTree() :m_root(nullptr), m_flag('*') { }
Node* Create(const char*& str);
int Size(Node* t);//计算结点个数
int Height(Node* t);//计算树的高度
};
int BTree::Size(Node* t)
{
if (t == nullptr)
{
return 0;
}
else
{
return Size(t->m_left) + Size(t->m_right) + 1;
}
}
int BTree::Height(Node* t)
{
if (t == nullptr)
{
return 0;
}
else
{
return Height(t->m_left) > Height(t->m_right) ? Height(t->m_left) + 1 : Height(t->m_right) + 1;
}
}
Node* BTree::Create(const char*& str)
{
if (*str == m_flag)
{
return nullptr;
}
else
{
//根据先序遍历,先创建根
Node* node = new Node(*str);//创建一个新结点
//创建左子树
node->m_left = Create(++str);
//创建右子树
node->m_right= Create(++str);
return node;
}
}
int main()
{
BTree bt;//定义了一棵树,树根为nullptr——m_root(nullptr)
const char * str = "ABD**EH***CF*I**G**";
bt.m_root = bt.Create(str);
cout << "size= " << bt.Size(bt.m_root) << endl;
cout << "height= " << bt.Height(bt.m_root) << endl
return 0;
}
运行结果为:
从图中也能看出来,树的结点个数为9,高度为4。