淘宝笔试的一个题目,在网上也没有找到正确的代码,自己写了一个,简单测试了下还行,但不保证完全正确。题目是这样的,一棵树,不一定是二叉树,是一棵很普通的屌丝树,把他镜像反转下。思路必须明确,首先用左孩子右兄弟的存储方法存储这棵树,然后对所有同级别的兄弟反转,也就是所有节点的右边链反转,其实也就是单链表反转的操作。代码中用到了递归,但是要注意的一点是单链表反转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; }