本篇为广东工业大学数据结构Anyview题库中的第六章的二叉树部分
头文件参阅请点击 -> 传送门
请注意,以下的题目顺序并不会按照题库中的题目顺序,想要快速查找题目请善用 Ctrl + F 呀
二叉树的遍历最快捷的还是使用递归来进行
但是,有时候为了提高效率,可以通过其他手段来对二叉树进行遍历操作
在此使用栈结构来进行对二叉树的遍历
代码如下(来自课本):
// 中序非递归遍历二叉树 T,visit 是对数据元素操作的应用函数
void InorderTraverse_T(BiTree T, Status(*visit) (TElemType e)) {
Stack S;
InitStack(S);
BiTree p;
// 找到最左下的结点,并将沿途结点的指针入栈 S
p = GoFarLeft(T, S);
while (p != NULL) {
// 访问结点
visit(p->data);
if (p->rchild != NULL) {
// 令 p 指向其右孩子为根的子树的最左下结点
p = GoFarLeft(p->rchild, S);
}
else if (StackEmpty(S) != TRUE) {
// 栈不空时退栈
Pop(S, p);
}
else {
// 栈空表明遍历结束
p = NULL;
}
}
}
BiTNode* GoFarLeft(BiTree T, Stack& S) {
// 从T结点出发,沿左分支走到底,沿途结点的指针入栈S,返回左下结点的指针
if (T == NULL) {
return NULL;
}
while (T->lchild != NULL) {
Push(S, T);
T = T->lchild;
}
return T;
}
解析(来自课本):
题目说明:
【题目】试利用栈及其基本操作写出二叉树T的非递归的先序遍历算法。二叉链表类型定义:
typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree;
可用栈类型Stack的相关定义:
typedef BiTree SElemType; // 栈的元素类型 Status InitStack(Stack &S); Status StackEmpty(Stack S); Status Push(Stack &S, SElemType e); Status Pop(Stack &S, SElemType &e); Status GetTop(Stack S, SElemType &e);
要求实现下列函数:
void PreOrder(BiTree T, void (*visit)(TElemType)); /* 使用栈,非递归先序遍历二叉树T, */ /* 对每个结点的元素域data调用函数visit */
先序遍历的顺序:中间结点 -> 左孩子 -> 右孩子
代码如下(来自本人):
void PreOrder(BiTree T, Status (*visit)(TElemType)){
// Add your code here
Stack stack;
InitStack(stack);
BiTree middleNode = T;
while (middleNode != NULL) {
// 1.先访问中间结点
visit(middleNode -> data);
// 2.因为顺序是:中 -> 左 -> 右,所以要先把右结点压入栈中保存(先进后出)
if (middleNode -> rchild != NULL) {
Push(stack, middleNode -> rchild);
}
// 3.把左结点压入栈中
if (middleNode -> lchild != NULL) {
Push(stack, middleNode -> lchild);
}
// 4.弹出,然后重复循环
if (StackEmpty(stack) != TRUE) {
Pop(stack, middleNode);
} else {
middleNode = NULL;
}
}
}
解析(来自本人):
不使用栈结构,但是可以使用栈的思想(?)
题目说明:
【题目】假设在三叉链表的结点中增设一个标志域(mark取值0,1或2)以区分在遍历过程中到达该结点时应继续向左或向右或访问该结点。试以此存储结构编写不用栈辅助的二叉树非递归后序遍历算法。带标志域的三叉链表类型定义:
typedef struct TriTNode { TElemType data; struct TriTNode *lchild, *rchild, *parent; int mark; // 标志域 } TriTNode, *TriTree;
要求实现以下函数:
void PostOrder(TriTree T, Status (*visit)(TElemType));
先序遍历的顺序:左孩子 -> 右孩子 -> 中间结点
- 使用数组(本质上还是使用栈的思想(先进后出))
- 没有用到三叉的特性(没有用到父节点parent)
- 重新定义标志域:0-表示还不用访问,1-表示要进行访问操作
代码如下(来自本人):
void PostOrder(TriTree T, Status (*visit)(TElemType)){
// Add your code here
// 使用数组(本质上还是使用栈的思想(先进后出))
// 没有用到三叉的特性(没有用到父节点parent)
// 使用到标志域:0-表示还不用访问,1-表示要进行访问操作
// array:定义一个数组,长度最小为树的所有结点数量(保证足够容纳所有结点)
TriTree array[36];
// pos:指示位置(相当于栈顶位置)
int pos = 0;
TriTree temp;
if (T == NULL) {
return ;
} else {
array[pos] = T;
}
while (pos >= 0) {
temp = array[pos];
if (temp -> mark == 1) {
visit(temp -> data);
pos--;
} else {
temp -> mark = 1;
// 先放入右结点
if (temp -> rchild != NULL) {
array[++pos] = temp -> rchild;
}
// 再放入左结点(因为遍历顺序是:左->右->中,而在这里取array中数据的顺序是“后进先出”)
if (temp -> lchild != NULL) {
array[++pos] = temp -> lchild;
}
// 如果是叶子结点,则直接进行访问操作
if (temp -> rchild == NULL && temp -> lchild == NULL) {
visit(temp -> data);
pos--;
}
}
}
return ;
}
题目说明:
【题目】编写递归算法:求二叉树中以元素值为x的结点为根的子树的深度。二叉链表类型定义:
typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree;
要求实现以下函数:
int Depthx(BiTree T, TElemType x); /* 求二叉树中以值为x的结点为根的子树深度 */
代码如下(来自本人):
int Depthx(BiTree T, TElemType x){
// Add your code here
// 补充一点:T 为 NULL 的话返回0
// ???为什么不在题目中说清楚呢?返回0或者返回-1(非法值)都有各自的合理说法啊
if (T == NULL) {
return 0;
}
int l_count = 0;
int r_count = 0;
if (T -> data == x) {
// 求深度的分支
if (T -> lchild == NULL && T -> rchild == NULL) {
return 1;
}
if (T -> lchild != NULL) {
l_count = Depthx(T -> lchild, T -> lchild -> data) + 1;
}
if (T -> rchild != NULL) {
r_count = Depthx(T -> rchild, T -> rchild -> data) + 1;
}
return l_count > r_count ? l_count : r_count;
} else {
// 找结点的分支
if (T -> lchild == NULL && T -> rchild == NULL) {
return 0;
}
if (T -> lchild != NULL) {
l_count = Depthx(T -> lchild, x);
}
if (T -> rchild != NULL) {
r_count = Depthx(T -> rchild, x);
}
return l_count > r_count ? l_count : r_count;
}
}
解析(来自本人):
首先一个前提是:会求一个二叉树的深度。
“求一个二叉树的深度”,用本题来描述就是“求二叉树中以根节点为根的子树的深度”。
所以说,本题和一般的“求二叉树深度”的区别就在于多了一个寻找“元素值为x的结点”的过程。
但是,但是,但是
因为题目要求使用递归算法,我们不可能在每一次进入递归之后都寻找一次“元素值为x的结点”,所以,要转变一下思路:
一棵深度为 k 且有 2k-1 个结点的二叉树称为满二叉树
一棵满二叉树如下所示:
一个约定:约定从根起,自上而下,自左而右,给满二叉树中的每个结点从 1 到 n 连续编号,编号为 i 的结点称为 i 结点(如上图所示)
深度为 k 且含 n 个结点的二叉树,如果其每个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应,则称为完全二叉树
一棵完全二叉树如下所示:
一棵非完全二叉树如下所示:
因为markdown的渲染问题,不能很好的地表现出树结构
可以看到,上面的非完全二叉树比完全二叉树少了 5、8、10 号结点
判定方法:用层次遍历算法遍历一棵完全二叉树的所有结点,如果中间过程中遇到了一个空结点(NULL),并且在此之后还有不为空的结点(不为NULL),则此二叉树不是完全二叉树;否则,此二叉树是一棵完全二叉树
题目说明:
【题目】编写算法判别给定二叉树是否为完全二叉树。二叉链表类型定义:
typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree;
可用队列类型Queue的相关定义:
typedef BiTree QElemType; // 设队列元素为二叉树的指针类型 Status InitQueue(Queue &Q); Status EnQueue(Queue &Q, QElemType e); Status DeQueue(Queue &Q, QElemType &e); Status GetHead(Queue Q, QElemType &e); Status QueueEmpty(Queue Q);
要求实现下列函数:
Status CompleteBiTree(BiTree T); /* 判别二叉树T是否为完全二叉树 */
代码如下(来自本人):
Status CompleteBiTree(BiTree T){
// Add your code here
if (T == NULL) {
return TRUE;
}
Queue queue;
InitQueue(queue);
EnQueue(queue, T);
BiTree temp;
bool hasNullBefore = false;
while (DeQueue(queue, temp) == OK) {
if (temp -> lchild != NULL) {
if (hasNullBefore) {
return FALSE;
} else {
EnQueue(queue, temp -> lchild);
}
} else {
hasNullBefore = true;
}
if (temp -> rchild != NULL) {
if (hasNullBefore) {
return FALSE;
} else {
EnQueue(queue, temp -> rchild);
}
} else {
hasNullBefore = true;
}
}
return TRUE;
}
解析(来自本人):