1、编写后序遍历二叉树的非递归算法。
算法思想:
void PostOrder(BiTree T)
{
InitStack(S);
p = T;
r = NULL;
while (p || !IsEmoty(S))
{
if (p)
{
push(S, p);
p = p->lchild;
}
else
{
GetTop(S, p);
if (p->rchild && p->rchild != r)
p = p->rchild;
else
{
pop(S, p);
visit(p->data);
r = p;
p = NULL;
}
}//else
}//while
}
2、试给出二叉树的自下而上、从右向左的层次遍历算法。
算法思想:利用原有的层次遍历算法,出队的同时将各结点指针入栈,在所有结点入栈后再从栈顶开始依次访问。
具体实现如下:
void InvertLevel(BiTree bt)
{
Stack s; Queue Q;
if (bt != NULL)
{
InitStack(s);
InitQueue(Q);
EnQueue(Q, bt);
while (IsEmpty(Q) == false)
{
DeQueue(Q, p);
Push(s, p);
if (p->lchild)
EnQueue(Q, p->lchild);
if (p->rchild)
EnQueue(Q, p->rchild);
}
while (IsEmpty(s) == false)
{
Pop(s, p);
visit(p->data);
}
}//if
}
3、假设二叉树采用二叉链表的存储结构,设计一个非递归算法求二叉树的高度。
算法思想:采用层次遍历的算法,设置变量level记录当前结点所在的层数,设置变量last指向当前层的最右结点,每次层次遍历出队时与last指针比较,若两者相等,则层数加1,并让last指向下一层的最右结点,直到遍历完成。level的值即为二叉树的高度。
int Btdepth(BiTree T)
{
if (!T)
return 0;
int front = -1, rear = -1;
int last = 0, level = 0;
BiTree Q[MaxSize];
Q[++rear] = T;
BiTree p;
while (front < rear)
{
p = Q[++front];
if (p->lchild)
Q[++rear] = p->lchild;
if (p->rchild)
Q[++rear] = p->rchild;
if (front == last)
{
level++;
last = rear;
}
}
return level;
}
求某层的结点个数、每层的结点个数、树的最大宽度等,都采用与此题类似的思想。当然,此题可编写递归算法,其实现如下:
int Btdepth2(BiTree T)
{
if (T == NULL)
return 0;
ldep = Btdepth2(T->lchild);
rdep = Btdepth2(T->rchild);
if (ldep > rdep)
return ldep + 1;
else
return rdep + 1;
}
4、设一棵二叉树中各结点的值互不相同,其先序遍历序列和中序遍历序列分别存于两个一维数组A[1...n]和B[1...n]中,试编写算法建立该二叉树的二叉链表。
算法思想:由先序序列和中序序列可以唯一确定一棵二叉树。
BiTree PreInCreat(ElemType A[], ElemType B[], int l1, int h1, int l2, int h2)
{
root = (BiTNode*)malloc(sizeof(BiTNode));
root->data = a[l1];
for (i = 12; B[i] != root->data; i++);
llen = i - 12;
rlen = h2 - i;
if (llen)
root->lchild = PreInCreat(A, B, l1 + 1, l1 + len, l2, l2 + llen - 1);
else
root->lchild = NULL;
if (rlen)
root->rchild = PreInCreat(A, B, h1 - rlen + 1, h1, h2 - rlen + 1, h2);
else
root->rchild = NULL;
return root;
}
5、二叉树按二叉链表形式存储,写一个判别给定二叉树是否是完全二叉树的算法。
算法思想:根据完全二叉树的定义,具有n个结点的完全二叉树与满二叉树中编号从1~n的结点一一对应。
采用层次遍历算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否有非空结点。若有,则二叉树不是完全树。
bool IsComplete(BiTree T)
{
InitQueue(Q);
if (!T)
return 1;
EnQueue(Q, T);
while (!IsEmpty(Q))
{
DeQueue(Q, p);
if (p)
{
EnQueue(Q, p->lchild);
EnQueue(Q, p->rchild);
}
else
while (!IsEmpty(Q))
{
DeQueue(Q, p);
if (p)
return 0;
}
}
return 1;
}
6、假设二叉树采用二叉链表存储结构存储,试设计一个算法,计算一棵给定二叉树的所有双分支结点个数。
算法思想:本题可以设置一个全局变量Num,每遍历到一个结点时,判断每个结点是否为分支结点(左、右结点都不为空,注意是双分支),若是则Num++。
7、设树B是一棵采用链式结构存储的二叉树,编写一个把树B中所有结点的左、右子树进行交换的函数。
算法思想:采用递归算法实现交换二叉树的左、右子树,首先交换b结点的左孩子的左、右子树,然后交换b结点的右孩子的左、右子树,最后交换b结点的左、右孩子,当结点为空时递归结束(后序遍历思想)。
void swap(BiTree b)
{
if (b)
{
swap(b->lchild);
swap(b->rchild);
temp = b->lchild;
b->lchild = b->rchild;
b->rchild = temp;
}
}
8、假设二叉树采用二叉链存储结构存储,设计一个算法,求先序遍历序列中第k(1<=k<=二叉树中结点个数)个结点的值。
算法思想:设置一个全局变量 i 记录已访问过的结点的序号,其初值是根结点在先序序列中的序号,即1。当二叉树b为空时返回特殊字符'#',当i==k时,表示找到了满足条件的结点,返回b->data;当i!=k时,递归地在左子树中查找,若找到则返回该值,否则继续递归地在左子树中查找,并返回其结果。
ElemType PreNode(BiTree b, int k)
{
if (b == NULL)
return '#';
if (i == k)
return b->data;
i++;
ch = PreNode(b->lchild, k);
if (ch != '#')
return ch;
ch = PreNode(b->rchild, k);
return ch;
}
本题实际上就是一个遍历算法的实现,只不过用一个全局变量来记录访问的序号,求其他遍历序列的第k个结点也采用相似的方法。
9、已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为x的结点,删去以它为根的子树,并释放相应的空间。
算法思想:
删除以元素值x为根的子树,只要能删除其左、右子树,就可以释放值为x的根结点,因此宜采用后序遍历。
删除值为x的结点,意味着应将其父结点的左(右)子女指针置空,用层次遍历易于找到某结点的父结点。本题要求删除树中每个元素值为x的结点的子树,因此要遍历完整棵二叉树。
void DeleteXTree(BiTree& bt)
{
if (bt)
{
DeleteXTree(bt->lchild);
DeleteXTree(bt->rchild);
free(bt);
}
}
//在二叉树上查找所有以x为元素值的结点,并删除以其为根的子树
voidSearch(BiTree bt, ElemType x)
{
BiTree Q[];
if (bt)
{
if (bt->data == x)
{
DeleteXTree(bt);
exit(0);
}
Init Queue(Q);
EnQueue(Q);
while (!IsEmpty(Q))
{
DeQueue(Q, p);
if (p->lchild)
if (p->lchild->data == x)
{
DeleteXTree(p->lchild);
p->lchild = NULL;
}
else
EnQueue(Q, p->lchild);
if (p->rchild)
if (p->rchild->data == x)
{
DeleteXTree(p->rchild);
p->rchild = NULL;
}
else
EnQueue(Q, p->rchild);
}
}
}
10、在二叉树中查找值为x的结点,试编写算法(用C语言)打印值为x的结点的所有祖先,假设值为x的结点不多于一个。
算法思想:采用非递归后序遍历,最后访问根结点,访问到值为x的结点时,栈中所有元素均为该结点的祖先,依次出栈打印即可。
typedef struct
{
BiTree t;
int tag;
}stack;
//在二叉树bt中,查找值为x的结点,并打印其所有祖先
void Search(BiTree bt, ElemType x)
{
stack s[];
top = 0;
while (bt != NULL || top > 0)
{
while (bt != NULL && bt->data != x)
{
s[++top].t = bt;
s[top].tag = 0;
bt = bt->lchild;
}
if (bt->data == x)
{
printf("所查结点的所有祖先结点的值为:\n");
for (i = 1; i <= top; i++)
printf("%d", s[i].t->data);
exit(1);
}
while (top != 0 && s[top].tag == 1)
top--;
if (top != 0)
{
s[top].tag = 1;
bt = s[top].t->rchild;
}
}//while(bt!=NULL||top>0)
}
11、设一棵二叉树的结点结构为(LINK,INFO,PLINK),ROOT为指向该二叉树根结点的指针,p和q分别为指向该二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT,p,q,r),找到p和q的最近公共祖先结点r。
算法思想:后序遍历最后访问根结点,即在递归算法中,根是压在栈底的。本题要找p和q的最近公共祖先结点r,不失一般性,设p在q的左边。
采用后序非递归算法,栈中存放二叉树结点的指针,当访问到某结点时,栈中所有元素均为该结点的祖先。后序遍历必然先遍历到结点p,栈中元素均为p的祖先。先将栈复制到另一辅助栈中。继续遍历到结点q时,将栈中元素从栈顶开始逐个到辅助栈中去匹配,第一个匹配(即相等)的元素就是结点p和q的最近公共祖先。
typedef struct
{
BiTree t;
int tag;
}stack;
stack s[], s1[];
//本算法求二叉树中p和q指向结点的最近公共结点
BiTree Ancestor(BiTree ROOT, BiTNode* p, BiTNode* q)
{
top = 0; bt = ROOT;
while (bt != NULL || top > 0)
{
while (bt != NULL)
{
s[++top].t = bt;
s[top].tag = 0;
bt = bt->lchild;
}
//假定p在q的左侧,遇到p时,栈中元素均为p的祖先
while (top != 0 && s[top].tag == 1)
{
if (s[top].t == p)
{
for (i = 1; i < top; i++)
s1[i] = s[i];
top1 = top;
}
if (s[top].t == q)
for (i = top; i > 0; i--)
{
for (j = top1; j > 0; j--)
if (s1[j].t == s[i].t)
return s[i].t;
}
top--;
}//while(top != 0 && s[top].tag == 1)
if (top != 0)
{
s[top].tag = 1;
bt = s[top].t->rchild;
}
}//while(bt != NULL || top > 0)
return NULL;
}
12、假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点树最多的那一层的结点个数)。
算法思想:采用层次遍历的方法求出所有结点的层次,并将所有结点和对应的层次放在一个队列中。然后通过扫描队列求出各层的结点总数,最大的层结点总数即为二叉树的宽度。
typedef struct
{
BiTree data[MaxSize];
int level[MaxSize];
int front, rear;
}Qu;
int BTWidth(BiTree b)
{
BiTree p;
int k, max, i, n;
Qu.front = Qu.rear = -1;
Qu.rear++;
Qu.data[Qu.rear] = b;
Qu.level[Qu.rear] = 1;
while (Qu.front < Qu.rear)
{
Qu.front++;
p = Qu.data[Qu.front];
k = Qu.level[Qu.front];
if (p->lchild != NULL)
{
Qu.rear++;
Qu.data[Qu.rear] = p->lchild;
Qu.level[Qu.rear] = k + 1;
}
if (p->rchild != NULL)
{
Qu.rear++;
Qu.data[Qu.rear] = p->rchild;
Qu.level[Qu.rear] = k + 1;
}
}//while
max = 0; i = 0;
k = 1;
while (i <= Qu.rear)
{
n = 0;
while (i <= Qu.rear && Qu.level[i] == k)
{
n++;
i++;
}
k == Qu.level[i];
if (n > max)
max = n;
}
return max;
}
本题队列中的结点,在出队后仍需要保留在队列中,以便求二叉树的宽度,所以设置的队列采用非环形队列,否则在出队后可能被其他结点覆盖,无法再求二叉树的宽度。
13、设有一棵满二叉树(所有结点值均不同),已知其先序序列为pre,设计一个算法求其后序序列post。
算法思想:对一般二叉树,仅根据先序或后序序列,不能确定另一个遍历序列。但对满二叉树,任意一个极点的左、右子树均含有相等的结点数,同时,先序序列的第一个结点作为后序序列的最后一个结点,由此得到将先序序列pre[l1..h1]转换为后序序列post[l2..h2]的递归模型如下:
14、设计一个算法将二叉树的叶结点按从左到右的顺序连成一个单链表,表头指针为head。二叉树按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表指针。
算法思想:通常我们所用的先序、中序和后序遍历对于叶结点的访问顺序都是从左到右,这里我们选择中序递归遍历。
设置前驱结点指针pre,初始为空。第一个叶结点由指针head指向,遍历到叶结点时,就将它前驱的rchild指针指向它,最后一个叶结点的rchild为空。
LinkedList head, pre = NULL;
LinkedList InOrder(BiTree bt)
{
if (bt)
{
InOrder(bt->lchild);
if(bt->lchild==NULL&&bt->rchild==NULL)
if (pre == NULL)
{
head = bt;
pre = bt;
}
else
{
pre->rchild = bt;
pre = bt;
}
InOrder(bt->rchild);
pre->rchild = NULL;
}
return head;
}
上述算法的时间复杂度为O(n),辅助变量使用head和pre,栈空间复杂度为O(n)。
15、试设计判断两棵二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都有一个根结点;或T1的左子树和T2的左子树是相似的,且T1的右子树和T2的右子树是相似的。
算法思想:本题采用递归的思想求解,若T1和T2都是空树,则相似;若有一个为空另一个不空,则必然不相似;否则递归地比较它们的左、右子树是否相似。递归函数的定义如下:
(1)f(T1,T2)=1;若T1==T2==NULL。
(2)f(T1,T2)=0;若T1和T2之一为NULL,另一个不为NULL。
(3)f(T1,T2)=f(T1->lchild,T2->lchild)&&f(T1->rchild,T2->rchild);若T1和T2均不为NULL。
int similar(BiTree T1, BiTree T2)
{
int leftS, rightS;
if (T1 == NULL && T2 == NULL)
return 1;
else if (T1 == NULL || T2 == NULL)
return 0;
else
{
leftS = similar(T1->lchild, T2->lchild);
rightS = similar(T1->lchild, T2->rchild);
return leftS && rightS;
}
}
16、写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法。
算法思想:在后序序列中,若结点p右右子女,则右子女是其前驱,若无右子女而有左子女,则左子女是其前驱。若结点p左、右子女均无,设其中序左线索指向某祖先结点f(p是f右子树中按中序遍历的第一个结点),若f有左子女,则其左孩子是结点p在后序下的前驱;若f无左孩子,则顺其前驱找双亲的双亲,一直找到双亲有左孩子(这时左孩子是p的前驱)。还有一种情况,若p是中序遍历的第一个结点,则结点p在中序和后序下均无前驱。
BiThrTree InPostPre(BiThrTree t, BiThrTree p)
{
BiThTree q;
if (p->rtag == 0)
q = p->rchild;
else if (p->ltag == 0)
q = p->lchild;
else if (p->lchild == NULL)
q = NULL;
else
{
//顺左线索向上找p的祖先,若存在,再找祖先的左孩子
while (p->ltag == 1 && p->lchild != NULL)
p = p->lchild;
if (p->ltag == 0)
q = p->lchild;
else
q = NULL;
}
return q;
}