淘宝笔试的一个题目,在网上也没有找到正确的代码,自己写了一个,简单测试了下还行,但不保证完全正确。题目是这样的,一棵树,不一定是二叉树,是一棵很普通的屌丝树,把他镜像反转下。思路必须明确,首先用左孩子右兄弟的存储方法存储这棵树,然后对所有同级别的兄弟反转,也就是所有节点的右边链反转,其实也就是单链表反转的操作。代码中用到了递归,但是要注意的一点是单链表反转while中操作的次数为链表的长度减一的,这也就是说递归的时候是缺少了一次的,这就要求在while后面补上这次缺少的递归操作。树的图如下:
树的左孩子(child)右兄弟(sibling)的存储如下:
代码如下:代码中的数组为树的左孩子右兄弟存储的先序遍历顺序。
#include <iostream>
#include <assert.h>
using namespace std;
typedef char ElemType;
typedef struct CSNode
{
ElemType data;
struct CSNode *firstchild, *nextsibling;
}CSNode, *CSTree;
const char INVALID = '^';
const int MAX_NODE_COUNT = 20;
void createCSTree(CSTree &tree, char *a);
void preOrder(const CSTree tree);
void mirrorCSTree(CSTree tree);
int main()
{
char a[] = {'A', 'B', 'E', 'H', '^', 'I', '^', '^', '^', 'C', '^', 'D', 'F', 'J', '^', '^', 'G', '^', '^', '^', '^', '\0'};
CSTree tree = NULL;
createCSTree(tree, a);
printf("before mirror preOrder:\n");
preOrder(tree);
mirrorCSTree(tree);
printf("after mirror preOrder:\n");
preOrder(tree);
return 0;
}
void createCSTree(CSTree &tree, char *a)
{
static int i = 0;
if(a[i] == '\0')
return;
if(a[i] == INVALID)
{
tree = NULL;
i++;
return;
}
else
{
CSNode *pCSNode = (CSNode *)malloc(sizeof(CSNode));
pCSNode->data = a[i++];
tree = pCSNode;
createCSTree(tree->firstchild, a);
createCSTree(tree->nextsibling, a);
}
}
void preOrder(const CSTree tree)
{
if(tree != NULL)
{
cout<<tree->data<<endl;
preOrder(tree->firstchild);
preOrder(tree->nextsibling);
}
}
void mirrorCSTree (CSTree tree)
{
if (tree == NULL)
return;
if (tree->firstchild)
{
CSTree p1 = tree->firstchild;
CSTree p2 = p1->nextsibling;
p1->nextsibling = NULL; //注意一开始的要先指向NULL
while (p2)
{
CSTree p3 = p2->nextsibling;
p2->nextsibling = p1;
mirrorCSTree (p1->firstchild);
if(p3 == NULL)
mirrorCSTree(p2);
p1 = p2;
p2 = p3;
}
tree->firstchild = p1;
}
}
其实树的镜像是非常难写正确的的. 下面是第二个版本,参数传进来的树的根节点。看起来比上面的顺眼些
树的结构和上面的一样,前序遍历为1, 2, 3, 4, 5, 6, 7, 8, 9, 10镜像后为1 7 10 8 9 6 2 3 5 4难的地方在当旋转右子树的最后一个节点的时候。当跳出while循环的时候,最后一个节点的left并没有调用mirror函数,就要想办法把它补上。最好的地方是加在while循环的里面也就是上面代码一样。如果加在while循环的后面,这个时候可能会出现重复的递归,递归不能返回的情况(非常可能出现)。我尝试了很多条件都没有使得递归停止。原因是如果在while后面加上一个mirrorTree(p1)的话,其实首先p1虽然变成p2了,但是p2的right已经改变了。这就非常麻烦了,最后的结果可能是在p1和p2之间来回递归。
void mirrorTree(BTree &rt)
{
if(rt == NULL)
return;
if(rt->right == NULL)
{
mirrorTree(rt->left);
return;
}
BTree p1= rt;
BTree p2= p1->right;
p1->right = NULL;
while(p2)
{
BTree p3 = p2->right;
p2->right = p1;
mirrorTree(p1->left);
if(p3 == NULL)
mirrorTree(p2->left);
p1 = p2;
p2 = p3;
}
rt = p1;
}
int main()
{
int a[] = {1, 2, 3, 4, -1, 5, -1, -1,-1, 6, -1, 7, 8, 9, -1, -1, 10, -1, -1, -1, -1};
int i = 0;
BTree rt = NULL;
createTree(a, i, rt);
preOrder(rt);
cout<<endl;
mirrorTree(rt);
preOrder(rt);
cout<<endl;
}