树
一 、树的基本概念
1-1 定义
树是一种递归定义的数据结构,是具有n(n >= 0)个结点的有限集合。n=0 时,该树称为空树。对于任意一颗非空树。
1-2 基本术语
双亲结点和兄弟结点
对于每一颗树来说,除根节点外的每一个结点都有一个唯一的前驱结点,称为双亲结点。
同一个双亲结点的两个或多个结点,这些具有同一双亲结点的结点互为兄弟结点。
结点的度和树的度
树中一个结点的孩子个数,称为该结点的度。
树中结点的最大度数,称为该树的度。
结点的层次、深度和高度
结点的层次:从树的根节点算起(根节点通常为第1层),该节点所在的层次
结点的深度:从根节点开始往下算起
结点的高度:从最下层的叶子结点开始往上算起
树的高度(或深度)
树中结点的最大层次
有序树与无序树
有序树:树的各结点之间是有次序的,不能互换
反之,则为无序树
路径与路径长度
树中两个结点之间的路径是由这两个结点之间所经过的结点的序列所构成的
路径长度:是路径上所经过的边的个数
树的基本性质
二、二叉树
2-1 二叉树的定义
二叉树是一颗结点的度不大于2的树,并且二叉树是一颗有序树,其左右孩子的顺序不可互换。
2-2 二叉树的存储结构
2-2-1 顺序存储-数组存储
满二叉树 :除最后一层的结点度为0,其他层的结点度都为2
二叉树的链式存储主要用以存储完全二叉树和满二叉树
2-2-2 链式存储-二叉链表存储法
//二叉树
typedef struct BitNode
{
/* data */
ElemType data;
struct BitNode *L_Child, *R_Child;//左孩子指针与右孩子指针
}BitNode, *BitTree;//注意BitTree 与 BitNode 类型的区别
三、二叉树的遍历操作
3-1 先序遍历
3-1-1 先序遍历的递归实现
//前序遍历
void PreOrder(BitTree T)
{
if(T != NULL)
{
visitTree_BitNode(T); //访问根节点
PreOrder(T->L_Child); //访问左孩子
PreOrder(T->R_Child); //访问右孩子
}
}
3-1-2 先序遍历的非递归实现
//前序遍历的非递归实现
void PreOrder_S(BitTree T)
{
stack<BitTree> s; //定义栈
BitTree p = T; //存储根节点
while (p || !s.empty())
{
if(p) //p非空
{
visitTree_BitNode(p);
s.push(p);
p = p->L_Child; //向左访问
}
else //p为空
{
p = s.top();
s.pop(); //将栈顶元素出栈
p = p->R_Child;
}
}
}
3-2 中序遍历
3-2-1 中序遍历的递归实现
//中序遍历
void InOrder(BitTree T)
{
if(T != NULL)
{
InOrder(T->L_Child); //访问左孩子
visitTree_BitNode(T); //访问根节点
InOrder(T->R_Child); //访问右孩子
}
}
3-2-2 中序遍历的非递归实现(依靠栈)
//中序遍历的非递归实现
void InOrder_S(BitTree T)
{
stack<BitTree> s; //定义栈
BitTree p = T; //存储根节点
while(p || !s.empty()) //结点非空 或栈非空
{
if (p) //p!空 将该结点压入栈中 一直向左遍历
{
s.push(p);
p = p->L_Child; //指向左子树
}
else //左子树为空
{
p = s.top();
s.pop();
visitTree_BitNode(p); //访问出栈结点
p = p->R_Child; //指向右子树
}
}
}
3-3 后序遍历
3-3-1 后序遍历的递归操作
//后序遍历
void PostOrder(BitTree T)
{
if(T != NULL)
{
PostOrder(T->L_Child); //访问左孩子
PostOrder(T->R_Child); //访问右孩子
visitTree_BitNode(T); //访问根节点
}
}
3-3-2 后序遍历的非递归操作(依靠栈)
//后序遍历的非递归实现
void PostOrder_S(BitTree T)
{
stack<BitTree> s;
BitTree p = T;
BitTree r = NULL;
while (p || !s.empty())
{
if(p) //p! 空 访问左子树
{
s.push(p);
p = p->L_Child; //走到最左
}
else //左子树为空
{
p = s.top(); //获取栈顶指针
if(p->R_Child && p->R_Child != r) //p指针非空 且 p不是上一次访问过的结点
{
p = p->R_Child;//指向右子树
s.push(p); //入栈
p = p->L_Child; //再次走到最左
}
else //p指针空 或 p是上一次访问过的结点
{
p = s.top();// //获取栈顶指针
s.pop();
visitTree_BitNode(p);//访问该节点
r = p;
p = NULL;
}
}
}
}
3-4 层次遍历
3-4-1 层次遍历-顺序:自上而下,自左至右(依靠队列)
/*层次遍历:从上之下,从左至右*/
void Level_Order(BitTree T)
{
queue<BitTree> Q;//定义一个队列
BitTree p = T;
Q.push(p);
while (!Q.empty())//若队列不为空
{
p = Q.front();//获取队头元素
Q.pop(); //队头元素出队
visitTree_BitNode(p); //对元素的访问
//将队头元素的左右孩子分别入队
//判空并入队
if(p->L_Child != NULL) Q.push(p->L_Child);
if(p->R_Child != NULL) Q.push(p->R_Child);
}
}
3-4-2 层次遍历-顺序:自下而上,自右至左 (依靠栈与队列)
/*层次遍历:从下之上,从右至左*/
void Level_Order_S(BitTree T)
{
stack<BitTree> S;//定义一个栈
queue<BitTree> Q;//定义一个队列
BitTree p = T;
Q.push(p);
while (!Q.empty())//若队列不为空
{
p = Q.front();//获取队头元素
Q.pop(); //队头元素出队
S.push(p); //出队元素入栈
//将队头元素的左右孩子分别入队
//判空并入队
if(p->L_Child != NULL) Q.push(p->L_Child);
if(p->R_Child != NULL) Q.push(p->R_Child);
}
while (!S.empty())//将元素依次出栈
{
p = S.top();
visitTree_BitNode(p);
S.pop();
}
}
3-4-3 层次遍历求二叉树的深度
非递归实现
//求二叉树的高度
//利用队列与二叉树层序遍历的特性
int Higth_BitTree(BitTree T)
{
if(T == NULL)//如果树为空 返回高度为0
return 0;
int level = 0;
BitTree p = T;
BitTree last = T;
queue<BitTree> Q;
Q.push(p); //根节点入队
while (!Q.empty()) //队列非空
{
p = Q.front(); //获取队头结点 并使其出队
Q.pop();
if(p->L_Child != NULL) //左孩子存在,则入队
Q.push(p->L_Child);
if(p->R_Child != NULL) //右孩子存在,则入队
Q.push(p->R_Child);
//队头结点是 该层最右结点 则层数+1 且 让last 指向当前队尾指针(即下一层的最右结点)
if(p == last)
{
level ++; //则层数+1
last = Q.back(); //last 指向当前队尾指针(即下一层的最右结点)
}
}
return level; //返回层数
}
递归实现
//求二叉树的高度 递归实现
int Higth_BitTree_R(BitTree T)
{
if(T == NULL)
return 0;
int left_dep = Higth_BitTree_R(T->L_Child);//左子树深度
int right_dep = Higth_BitTree_R(T->R_Child);//右子树深度
return left_dep > right_dep ? left_dep+1 : right_dep+1;//树的高度等与子树的最大高度加根节点
}
3-4-4 层次遍历求二叉树的最大宽度
//求二叉树的宽度
//利用队列与二叉树层序遍历的特性
int Wide_BitTree(BitTree T)
{
if(T == NULL)//如果树为空 返回高度为0
return 0;
int wide = 0;
BitTree p = T;
BitTree last = T;
queue<BitTree> Q;
Q.push(p); //根节点入队
while (!Q.empty()) //队列非空
{
p = Q.front(); //获取队头结点 并使其出队
Q.pop();
if(p->L_Child != NULL) //左孩子存在,则入队
Q.push(p->L_Child);
if(p->R_Child != NULL) //右孩子存在,则入队
Q.push(p->R_Child);
if(p == last) //队头结点是 该层最右结点 则当前层的全部元素都已入队
{
//则获取当前层结点的个数并于当前记录的层最大节点数作比较
wide = wide < Q.size() ? Q.size() : wide;
last = Q.back(); //last 指向当前队尾指针(即下一层的最右结点)
}
}
return wide; //返回层数
}
3-4-5 层次遍历求二叉树某层的宽度(或结点个数)
/****************************************
* 求二叉树某层的结点个数
* 利用队列与二叉树层序遍历的特性
* 保证输入层数的合法性
***************************************/
int level_x_wide_BitTree(BitTree T, int level)
{
if(T == NULL)//如果树为空 返回高度为0
return 0;
if(level > Higth_BitTree(T))//保证level的合法性
{
cout<<"已超出此二叉树的高度!"<<endl;
return 0;
}
int index = 1; //层数标记量
BitTree p = T; //中间变量记录根节点位置
BitTree last = T; //记录每一层的最右侧的结点
queue<BitTree> Q; //创建一个队列
Q.push(p); //根节点入队
if(level == index) return index;
while (!Q.empty()) //队列非空
{
p = Q.front(); //获取队头结点 并使其出队
Q.pop();
if(p->L_Child != NULL) //左孩子存在,则入队
Q.push(p->L_Child);
if(p->R_Child != NULL) //右孩子存在,则入队
Q.push(p->R_Child);
if(p == last) //队头结点是 该层最右结点 则当前层的全部元素都已入队
{
index ++; //层数加1
if(index == level)
return Q.size(); //则返回当前层结点的个数
last = Q.back(); //last 指向当前队尾指针(即下一层的最右结点)
}
}
return Q.size(); //返回层数
}
3-5 访问操作
//访问操作
void visitTree_BitNode(BitTree &N)
{
/*code*/
cout<< N->data <<endl;//打印输出
}
四、二叉树的建立
4-1 先序、中序序列递归建立二叉树(二叉链表)
/*************************************************
* 递归建立法
* 根据数组中的 先序遍历序列和中序遍历序列建立二叉树 (要求二叉树中不含相同元素的值)
* 参数: Pre_order[] 先序遍历(序列下标从0开始)
* In_order[] 中序遍历
* pre_first 先序的第一个结点
* pre_last 先序的最后一个结点
* in_first 中序的第一个结点
* in_last 中序的最后一个结点
* 初始值 pre_first = in_first= 0
* in_last = pre_last= n-1
* ************************************************/
BitTree Pre_In_Creat_BitTree(ElemType Pre_order[], ElemType In_order[],
int pre_first, int pre_last,
int in_first, int in_last)
{
BitTree T = new BitNode; //创建根节点
T->data = PreOrder[pre_first]; //赋值
int i;
for(i = in_first; In_order[i] != T->data; i++); //找到中序序列中个根节点的位置
int left_len = i - in_first; //左子树序列长度
int right_len = in_last - i; //右子树序列长度
if(left_len != 0)
T->L_Child = Pre_In_Creat_BitTree(Pre_order[], In_order[], //根节点T 左边序列
pre_first + 1, pre_first + left_len,
in_first , in_first + left_len -1);
else
T->L_Child = NULL;//左子树为空
if(right_len != 0)
T->R_Child = Pre_In_Creat_BitTree(Pre_order[], In_order[], //根节点T 右边序列
pre_last -right_len + 1, pre_last,
in_last - right_len + 1, in_last);
else
T->R_Child = NULL;//左子树为空
return T;
}
4-2 后序、中序递归建立二叉树(二叉链表)
待续
4-3 层序、中序递归建立二叉树(二叉链表)
待续
五、哈夫曼树与哈夫曼编码(二叉树)
待续
六、线索二叉树
待续
七、m叉树的表示方法
7-1 孩子兄弟表示法
待续
随缘待续
【1】参考王道数据结构