目录
一、二叉树的定义
1、二叉树:是n(n≥0)个结点的有限集合。n=0的树称为空二叉树;n>0的二叉树由一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成 。
逻辑结构: 一对二(1:2)
基本特征:
① 每个结点最多只有两棵子树(不存在度大于2的结点);
② 左子树和右子树次序不能颠倒。所以下面是两棵不同的树
注意:二叉树不是有序树
2、满二叉树:在一棵二叉树中,如果所有分支结点都存在左 子树和右子树,并且所有叶子结点都在同一层上,这样的 二叉树称为满二叉树。
3、完全二叉树:如果一棵深度为k,有n个结点的二叉树中各 结点能够与深度为k的顺序编号的满二叉树从0到n-1标号 的 结点相对应的二叉树称为完全二叉树。
二、 二叉树的链式存储结构
二叉树的链式存储结构是用指针建立二叉树中结点之间的关系。二叉树最常用的的链式存储结构是二叉链。二叉链存储结构的每个结点包含三个域,分别是数据域data、左孩子指针域leftChild和右孩子指针域rightChild。二叉链存储结构中每个结点的图示结构为:
二叉链存储结构的二叉树也有不带头结点和带头结点两种。带头结点的二叉链存储结构的二叉树见下图(b),不带头结点的二叉链存储结构的二叉树如下图(a)所示。
三、二叉链结点定义
typedef struct Node
{ DataType data;
struct Node *leftChild;
struct Node *rightChild;
}BiTreeNode;
四、二叉树的创建
可以证明,给定一棵二叉树的前序遍历序列和中序遍历序列可以惟一确定一棵二叉树的结构。
可以证明,给定一棵二叉树的后序遍历序列和中序遍历序列可以惟一确定一棵二叉树的结构。
但是,给定一棵二叉树的后序遍历序列和前序遍历序列不能惟一确定一棵二叉树的结构。
1、利用中序,前序遍历建二叉树
算法思路:
1,如果当前的遍历序列里是空的,直接返回。
2,如果当前的遍历序列里只有一个元素, 直接作为叶子结点; 返回;
3, 创建子树的根节点;
4, 找到中序遍历的根结点, 分别构造左右子树的中序前序遍历
5, 递归构造左子树
6,递归构造右子树
BiTreeNode * tree_buld(string pre, string mid)//利用中序,前序遍历建二叉树
{
if (pre.size() == 0) return NULL;
if (pre.size() == 1) {
BiTreeNode *root = (BiTreeNode *)malloc(sizeof(BiTreeNode));
root->data = pre[0];
root->leftChild = NULL;
root->rightChild = NULL;
return root;
}
BiTreeNode *root = (BiTreeNode *)malloc(sizeof(BiTreeNode));
root->data = pre[0];
int i;
for (i = 0; i < mid.size(); i++)
if (mid[i] == pre[0])
break;
string pre1 =pre.substr(1,i) ;
string pre2 = pre.substr(i + 1, mid.size());
string mid1 = mid.substr(0, i);
string mid2 = mid.substr(i+1, mid.size());
root->leftChild = tree_buld(pre1,mid1);
root->rightChild = tree_buld(pre2,mid2);
return root;
}
2、利用中序,后序遍历建二叉树
算法思路:
1,如果当前的遍历序列里是空的,直接返回。
2,如果当前的遍历序列里只有一个元素, 直接作为叶子结点; 返回;
3, 创建子树的根节点;
4, 找到中序遍历的根结点, 分别构造左右子树的中序后序遍历
5, 递归构造左子树
6,递归构造右子树
BiTreeNode * tree_buld_mp(string post, string mid)//利用中序,后序遍历建二叉树
{
if (post.size() == 0) return NULL;
if (post.size() == 1) {
BiTreeNode *root = (BiTreeNode *)malloc(sizeof(BiTreeNode));
root->data = post[post.size()-1];
root->leftChild = NULL;
root->rightChild = NULL;
return root;
}
BiTreeNode *root = (BiTreeNode *)malloc(sizeof(BiTreeNode));
root->data = post[post.size() - 1];
int i;
for (i = 0; i < mid.size(); i++)
if (mid[i] == post[post.size() - 1])
break;
string post1 = post.substr(0, i);
string post2 = post.substr(i, post.size()-i-1);
string mid1 = mid.substr(0, i);
string mid2 = mid.substr(i+1, mid.size());
root->leftChild = tree_buld_mp(post1, mid1);
root->rightChild = tree_buld_mp(post2, mid2);
return root;
}
3、前序与后序遍历不能确定一棵二叉树
这两棵二叉树是不同的,但前序都是·ABC, 后序都是CBA,因此前序与后序遍历不能确定一棵二叉树
五、二叉树的非递归遍历
所有递归算法都可以借助堆栈转换成循环结构的非递归算法,通常有两种方法:一种方法是形式化模拟转换,另一种方法是根据要求解问题的特点设计借助堆栈的循环结构算法。
非递归的二叉树前序遍历算法如下:
(1)初始化设置一个堆栈;
(2)把根结点指针入栈;
(3)当堆栈非空时,循环执行步骤(3.a)到步骤(3.c);
(3.a)出栈取得一个结点指针,访问该结点;
(3.b)若该结点的右子树非空,则将该结点的右子树指 针入栈;
(3.c)若该结点的左子树非空,则将该结点的左子树指 针入栈;
(4)结束。
void preorder_non_recursion(BiTreeNode* root)
{
stack<BiTreeNode*>s;
cout << "preorder_non_recursion \n";
if (root == NULL) return;
s.push(root);
while (!s.empty())
{
BiTreeNode* node = s.top(); s.pop();
cout << node->data<<"\t";
if (node->rightChild != NULL) s.push(node->rightChild);//先将右子树进栈
if (node->leftChild != NULL) s.push(node->leftChild);
}
cout << endl;
}
非递归的二叉树中序遍历算法如下:
a)从根节点开始检索,如果当前节点不为空,则将当前节点入栈,让当前节点成为其左孩子节点,再继续上一步的操作
b)加入当前节点为空了,说明其父节点已经没有左孩子了,那么将栈顶元素出栈并输出
c) 判断栈顶元素是否有右孩子,如果有右孩子,则进入它的右孩子,跳出,重复第一步;如果没有右孩子,则继续将栈顶元素出栈
d) 当栈为空,并且当前节点为空时,遍历完成,退出
void midorder_non_recursion(BiTreeNode* root)
{
stack<BiTreeNode*>s;
cout << "midorder_non_recursion \n";
if (root == NULL) return;
BiTreeNode * t = root;
while (t || !s.empty())
{
while (t) {//找到最左边的子树
s.push(t); t = t->leftChild;
}
while(!s.empty())
{
t = s.top(); s.pop();
cout << t->data << "\t"; //输出栈顶元素
t = t->rightChild; //进入右子树
if (t) break; //t非空跳出,回到上面,进入左子树; t为空,就继续出栈
}
}
cout << endl;
}
非递归的二叉树后序遍历算法如下:
void postorder_non_recursion(BiTreeNode* root)
{
stack<BiTreeNode*>s;
cout << "postorder_non_recursion \n";
if (root == NULL) return;
BiTreeNode * t = root, *pre=NULL;
while (t || !s.empty())
{
while (t) {//找到最左边的子树
s.push(t); t = t->leftChild;
}
while (!s.empty())
{
t = s.top(); s.pop();
if (t->rightChild == NULL || t->rightChild == pre)
{
cout << t->data << "\t"; //输出栈顶元素,且右子树为空
pre = t;
}
else
{
s.push(t);
t = t->rightChild; //进入右子树
}
}
}
cout << endl;
}
六,层序遍历
二叉树的层序遍历算法如下:
(1)初始化设置一个队列;
(2)把根结点指针入队列;
(3)当队列非空时,循环执行步骤(3.a)到步骤(3.c);
(3.a)出队列取得一个结点指针,访问该结点;
(3.b)若该结点的左子树非空,则将该结点的左子树指 针入队列;
(3.c)若该结点的右子树非空,则将该结点的右子树指 针入队列;
(4)结束。
void Level_order(BiTreeNode *root)//层序遍历
{
queue<BiTreeNode*>q; //用队列
cout << "level order" << endl;
if (root == NULL) return;
q.push(root);
while (!q.empty())//当队列非空
{
BiTreeNode *node = q.front(); q.pop();
cout << node->data <<"\t";
if (node->leftChild != NULL) q.push(node->leftChild);
if (node->rightChild!= NULL) q.push(node->rightChild);
}
cout << endl;
}
七、二叉树的高度
int Get_tree_Level(BiTreeNode *root)
{
if (root == NULL) return 0;
int left = 0, right = 0;
if (root->leftChild != NULL) left=1+Get_tree_Level(root->leftChild);
if (root->rightChild != NULL) right=1+Get_tree_Level(root->rightChild);
if (left >= right) return left; //从左右子树挑出高的
else return right;
}
八、求二叉树的叶子结点数目
int LeafNum(BiTreeNode *root )
{ if (root ==NULL) return(0);
else if (root ->leftChild==NULL && root ->rightchild==NULL)
return(1);
else return(LeafNum(root->leftChild)+LeafNum(root->rightchild));
}
九、在二叉树上统计节点值等于c的个数
int find_x2(BiTreeNode * root, char c)
{
if (root == NULL) return 0;
if (root->data == c)
return 1 + find_x2(root->leftChild, c)+ find_x2(root->rightChild, c);
else
return find_x2(root->leftChild, c) + find_x2(root->rightChild, c);
}
当一棵二叉树上节点值由多个值等于c, 最简单的方法是遍历,用全局变量去统计,但在一个大的工程中,尽量不要使用全局变量。 最简单的方法还是递归。