1. 求二叉树中距离根结点最近/最远的叶子结点。
求距离根结点最近的叶子结点
- 算法思想:采用基于先序遍历的递归方法,用min记录结点层次(初始值取一个大整数),表示当前访问结点的层次。先判断根结点,若为叶子结点,其层次l小于min,置min=l,用x记录其结点值;再到左子树中查找并比较;最后到右子树中查找并比较。
void MinLeaf(BiTNode* b, int l, int &min, char& x)
{
//l的初值为1,min取最大整数,x为所求的叶子结点
if (b == NULL) return;
else
{
if (b->lchild == NULL && b->rchild == NULL)
{
if (l < min)
{
min = l;
x = b->data;
}
}
MinLeaf(b->lchild, l + 1, min, x);
MinLeaf(b->rchild, l + 1, min, x);
}
}
求距离根结点最远的叶子结点
//l的初值为1,max取最小整数,y为所求的叶子结点
void MaxLeaf(BiTNode* b, int l, int& max, char& y)
{
if (b == NULL) return;
else
{
if (b->lchild == NULL && b->rchild == NULL)
{
if (l > max)
{
max = l;
y = b->data;
}
}
MaxLeaf(b->lchild, l + 1, max, y);
MaxLeaf(b->rchild, l + 1, max, y);
}
}
运行结果
2. 假设二叉树采用二叉链存储结构,求二叉树b中结点值为x的结点的层次(或者深度)。
- 算法思想:Level(BiTNode *b,ElemType x,int h),其返回值为在二叉树b中查找结点值为x的结点所在的层次,返回0表示未找到。如果b为空树,返回0;如果当前根结点的结点值为x,则返回h;否则在左子树中査找(层次h需要增1),若在左子树中未找到,再在右子树中查找(层次h需要增1)。
int Level(BiTNode* b, ElemType x, int h) //h置初值1
{
int lev;
if (b == NULL) return 0;
else if (b->data == x) return h;
else
{
lev = Level(b->lchild, x, h + 1); //在左子树中查找
if (lev != 0) return lev; //在左子树中找到了,返回1
else lev = Level(b->rchild, x, h + 1); //在左子树中未找到,再在右子树中查找
}
}
3. 输出从根结点到每个叶子结点的路径逆序列。
- 算法思想:利用后序遍历非递归算法的特点,将其中的访问结点改为判断该结点是否为叶子结点,若是,输出栈中的所有结点值。
void AllPath(BiTNode* b)
{
BiTNode* p, * r;
bool flag;
SqStack* st; //定义一个顺序栈指针st
InitStack(st); //初始化栈st
p = b;
do {
while (p != NULL) //扫描结点p的所有左下结点并进栈
{
Push(st, p); //结点p进栈
p = p->lchild; //移到到左孩子
}
r = NULL; //r指向刚访问的结点,初始时为空
flag = true; //flag为真表示正在处理栈顶结点
while (!EmptyStack(st) && flag)
{
GetTop(st, p); //取出当前的栈顶结点p
if (p->rchild == r) //若结点p的右孩子为空或者为刚刚访问过的结点
{
if (p->lchild == NULL && p->rchild == NULL)
{
for (int i = st->top; i > 0; i--)
printf("%c -> ", st->data[i]->data);
printf("%c\n", st->data[0]->data);
}
Pop(st, p);
r = p; //r指向刚访问过的结点
}
else
{
p = p->rchild; //转向处理其右子树
flag = false; //表示当前不是处理栈顶结点
}
}
} while (!EmptyStack(st)); //栈不空循环
printf("\n");
DestroyStack(st); //销毁栈
}
运行结果
4. 输出每个叶子结点到根结点的逆路径。
- 算法思想:采用基于先序遍历递归算法,用path数组存放查找的路径,pathlen存放路径长度,当找到叶子结点b时,由于b叶子结点尚未添加到path中,因此在输出路径时还需输出b->data值;若b不为叶子结点,将b->data放入path中,然后在左、右子树中递归查找。
void AllPath(BiTNode* b, ElemType path[], int pathlen)
{
if (b == NULL) return;
else
{
if (b->lchild == NULL && b->rchild == NULL) //b为叶子结点
{
printf("%c到根结点逆路径:%c ", b->data, b->data);
for (int i = pathlen - 1; i >= 0; i--) printf("%c ", path[i]);
printf("\n");
}
else
{
path[pathlen] = b->data; //将当前结点放入路径中
pathlen++; //路径长度+1
AllPath(b->lchild, path, pathlen); //递归扫描左子树
AllPath(b->rchild, path, pathlen); //递归扫描右子树
pathlen--; //恢复环境
}
}
}
运行结果
5. 输出该二叉树中第一条最长的/最短的路径长度,并输出此路径上各结点的值。
第一条最长的路径
- 算法思想:采用基于先序递归算法的思路。用path[ ]保存扫描到当前结点的路径,pathlen保存扫描到当前结点的路径长度,longpath保存最长的路径,longpathlen保存最长路径长度。当b为空时,表示当前扫描的一个分支已扫描完毕,将pathlen与longpathlen进行比较,将较长的路径及路径长度分别保存在longth[ ]和longpathlen中。
void LongPath(BiTNode* b, ElemType path[], int pathlen, ElemType longpath[], int& longpathlen)
{
//pathlen的初值为0,longpathlen的初值取最小整数
if (b == NULL)
{
if (pathlen > longpathlen) //若当前路径更长,将路径保存在longpath中
{
for (int i = pathlen - 1; i >= 0; i--) longpath[i] = path[i];
longpathlen = pathlen;
}
}
else
{
path[pathlen] = b->data; //将当前结点放入路径中
pathlen++; //路径长度+1
LongPath(b->lchild, path, pathlen, longpath, longpathlen); //递归扫描左子树
LongPath(b->rchild, path, pathlen, longpath, longpathlen); //递归扫描右子树
}
}
第一条最短的路径
void ShortPath(BiTNode* b, ElemType path[], int pathlen, ElemType shortpath[], int& shortpathlen)
{
//pathlen的初值为0,shortpathlen的初值取最大整数
if (b == NULL)
{
if (pathlen < shortpathlen) //若当前路径更短,将路径保存在shortpath中
{
for (int i = pathlen - 1; i >= 0; i--) shortpath[i] = path[i];
shortpathlen = pathlen;
}
}
else
{
path[pathlen] = b->data; //将当前结点放入路径中
pathlen++; //路径长度+1
ShortPath(b->lchild, path, pathlen, shortpath, shortpathlen); //递归扫描左子树
ShortPath(b->rchild, path, pathlen, shortpath, shortpathlen); //递归扫描右子树
}
}
运行结果
- 所谓最小枝长是指根结点到最近叶子结点的路径长度;最大枝长是指根结点到最远叶子结点的路径长度。
6. 求二叉树b的最小/最大枝长。所谓最小/最大枝长是指根结点到最近/最远叶子结点的路径长度。
二叉树b的最小枝长
- 算法思想:采用基于先序遍历的递归方法,先判断根结点不空,再求出左、右子树的最小枝长min1和min2,并返回MIN(min1,min2)+1。
int MinBranch(BiTNode* b) //求二叉树的最小枝长
{
int min1, min2, min;
if (b == NULL) return 0;
else
{
min1 = MinBranch(b->lchild);
min2 = MinBranch(b->rchild);
if (min1 < min2) min = min1 + 1;
else min = min2 + 1;
return min;
}
}
二叉树b的最大枝长
int MaxBranch(BiTNode* b) //求二叉树的求最大枝长
{
int max1, max2, max;
if (b == NULL) return 0;
else
{
max1 = MaxBranch(b->lchild);
max2 = MaxBranch(b->rchild);
if (max1 > max2) max = max1 + 1;
else max = max2 + 1;
return max;
}
}
运行结果
7. 将二叉树的顺序存储结构转换成二叉链存储结构。
- 算法思想:设二叉树的顺序存储结构为a,由f(a,i)返回创建的以a[i]为根结点的二叉链存储结构(初始调用为b=f(a,1))。
BiTNode* trans(SqTree t[], int i)
{
BiTNode* b;
if (i > MaxSize) return NULL;
if (t[i].value == '#') return NULL;
b = (BiTNode*)malloc(sizeof(BiTNode));
b->data = t[i].value;
b->lchild = trans(t, 2 * i);
b->rchild = trans(t, 2 * i + 1);
return b;
}
trans(t, 1);
运行结果
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
value | D | A | B | E | F | C | G | # | # | # | # | H | # | # | I |
8. 将二叉树的二叉链存储结构转换成顺序存储结构。
void trans(BiTNode* b, ElemType a, int i)
{
if (b == NULL) a[i] = '#';
else
{
a[i] = b->data;
trans(b->lchild, a, 2 * i);
trans(b->rchild, a, 2 * i + 1);
}
}
tans(b,a,1);
9. 假设二叉树采用二叉链表存储结构,求二叉树的高度。
- 算法思想:采用后序遍历的方法,当处理到最深层(h层)的叶子结点时,其h-1层的所有结点都在栈中,当前栈的深度即为二叉树的高度。
int PostOrder(BiTNode* b)
{
BiTNode* p, * r;
bool flag;
int count = 0, max = 0;
SqStack* st; //定义一个顺序栈指针st
InitStack(st); //初始化栈st
p = b;
do {
while (p != NULL) //扫描结点p的所有左下结点并进栈
{
Push(st, p); //结点p进栈
count++; //入栈则高度+1
if (count > max) max = count;
p = p->lchild; //移到到左孩子
}
r = NULL; //r指向刚访问的结点,初始时为空
flag = true; //flag为真表示正在处理栈顶结点
while (!EmptyStack(st) && flag)
{
GetTop(st, p); //取出当前的栈顶结点p
if (p->rchild == r) //若结点p的右孩子为空或者为刚刚访问过的结点
{
printf("%c ", p->data); //访问结点p
Pop(st, p);
count--; //出栈则高度-1
r = p; //r指向刚访问过的结点
}
else
{
p = p->rchild; //转向处理其右子树
flag = false; //表示当前不是处理栈顶结点
}
}
} while (!EmptyStack(st)); //栈不空循环
printf("\n");
//DestroyStack(st); //销毁栈
return max;
}
运行结果
- 时间复杂度为O(n),空间为O(max)。其中,n为二叉树中的结点个数,max为栈的最大深度。
- 算法思想:利用层序遍历思想,记录层数。rear表示每层中最后一个结点的序号,front表示当前访问结点的序号。
int LevelOrder(BiTNode* b)
{
int front = 0, rear = 0, last = 1, level = 0; //last指向当前层的最右结点
if (b == NULL) return 0; //空树返回0
BiTNode* p;
SqQueue* qu;
InitQueue(qu);
enQueue(qu, b); //根节点入队
rear++;
while (!EmptyQueue(qu)) //队不空,则循环
{
deQueue(qu, p); //队列元素出队,即正在访问的结点
front++;
printf("%c ", p->data);
if (p->lchild != NULL) //左孩子入队
{
enQueue(qu, p->lchild);
rear++;
}
if (p->rchild != NULL) //右孩子入队
{
enQueue(qu, p->rchild);
rear++;
}
if (front == last) //处理该层的最右结点
{
level++; //层数+1
last = rear; //last指向下层
}
}
return level;
}
运行结果
int Height(BiTNode* b)
{
int left, right;
if (b == NULL) return 0;
left = Height(b->lchild);
right = Height(b->rchild);
return left > right ? left + 1 : right + 1;
}
运行结果
- 求某层的结点个数、每层的结点个数、树的最大宽度等,都采用与此题类似的思想。
①某层的结点个数(非递归)
int LevelOrder(BiTNode* b,int i) //求二叉树的高度
{
//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
int front = 1, rear = 0, last = 1, level = 0, visit = 0;
if (b == NULL || i <= 0) return 0; //空树或参数不正确时返回0
if (b != NULL && i == 1) return 1;
BiTNode* p;
SqQueue* qu;
InitQueue(qu);
enQueue(qu, b); //根结点入队
rear++;
while (!EmptyQueue(qu)) //队不空,则循环
{
deQueue(qu, p); //队列元素出队,即正在访问的结点
visit++; //visit表示当前正在访问的结点的序号
//printf("%c ", p->data);
if (p->lchild != NULL)
{
enQueue(qu, p->lchild); //左孩子入队
rear++;
}
if (p->rchild != NULL)
{
enQueue(qu, p->rchild); //右孩子入队
rear++;
}
if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
{
level++; //层数+1
if (level == i)
{
printf("front=%d last=%d", front, last);
return last - front + 1;
}
front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
last = rear; //修改下一层最后一个结点的序号
}
}
}
运行结果:
①某层的结点个数(递归)
- 设计算法为Lnodenum(BiTNode* b, int h, int k, int& n),h表示b所指的结点层次,n是引用型参数,用于求第k层的结点个数。在初始调用时,b为根结点指针,h为1,n赋值为0,即调用方式是n=0;Lnodenum(b,1,k,n)。
void Lnodenum(BiTNode* b, int h, int k, int& n)
{
if (b == NULL) return; //空树直接返回
else //处理非空树
{
if (h == k) n++; //当前访问的结点在第k层时n增1
else if (h < k) //若当前结点层次小于k,递归处理左、右子树
{
Lnodenum(b->lchild, h + 1, k, n);
Lnodenum(b->rchild, h + 1, k, n);
}
}
}
②每层的结点个数(非递归)
int LevelOrder(BiTNode* b) //求二叉树的高度
{
//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
int front, rear = 0, last = 1, level = 0, visit = 0;
if (b == NULL) return 0; //空树返回0
BiTNode* p;
SqQueue* qu;
InitQueue(qu);
enQueue(qu, b); //根结点入队
rear++;
while (!EmptyQueue(qu)) //队不空,则循环
{
deQueue(qu, p); //队列元素出队,即正在访问的结点
visit++; //visit表示当前正在访问的结点的序号
//printf("%c ", p->data);
if (p->lchild != NULL)
{
enQueue(qu, p->lchild); //左孩子入队
rear++;
}
if (p->rchild != NULL)
{
enQueue(qu, p->rchild); //右孩子入队
rear++;
}
if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
{
level++; //层数+1
if(level == 1) front = last; //第一层只有根结点
printf("front=%d last=%d", front, last);
printf("-----第%d层有:%d个结点\n", level, last-front + 1);
front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
last = rear; //修改下一层最后一个结点的序号
}
}
return level;
}
运行结果:
③树的最大宽度(非递归)
int LevelOrder(BiTNode* b) //求二叉树的高度
{
//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
int front, rear = 0, last = 1, level = 0, visit = 0, maxwidth = 0;
if (b == NULL) return 0; //空树返回0
BiTNode* p;
SqQueue* qu;
InitQueue(qu);
enQueue(qu, b); //根结点入队
rear++;
while (!EmptyQueue(qu)) //队不空,则循环
{
deQueue(qu, p); //队列元素出队,即正在访问的结点
visit++; //visit表示当前正在访问的结点的序号
//printf("%c ", p->data);
if (p->lchild != NULL)
{
enQueue(qu, p->lchild); //左孩子入队
rear++;
}
if (p->rchild != NULL)
{
enQueue(qu, p->rchild); //右孩子入队
rear++;
}
if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
{
level++; //层数+1
if (level == 1) front = last; //第一层只有根结点
printf("front=%d last=%d", front, last);
printf("-----第%d层有:%d个结点\n", level, last - front + 1);
if (last - front + 1 > maxwidth) maxwidth = last - front + 1; //比较每一层的结点个数
front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
last = rear; //修改下一层最后一个结点的序号
}
}
printf("\n二叉树的高度为:%d", level);
return maxwidth;
}
运行结果: