二叉树的旋转操作和平衡判断
@Author: 张海拔
@Update: 2014-2-2
@Link: http://www.cnblogs.com/zhanghaiba/p/3537221.html
【问题描述】(问题描述摘自“http://ac.jobdu.com/problem.php?pid=1541”,略有修改)
旋转是二叉树的基本操作,我们可以对任意一个存在父亲节点的子节点进行旋转,包括如下几种形式(设被旋转节点为x,其父亲节点为p):
1.左旋
旋转前,x是p的右儿子。
x的左儿子(若存在)变为p的右儿子,p变为x的左儿子。如下图
2.右旋
旋转前,x是p的左儿子。
x的右儿子(若存在)变为p的左儿子,p变为x的右儿子。如下图
综上,我们可以通过检查选择前x是p的左儿子还是右儿子来判断该次旋转是左旋还是右旋。
给定一颗n个节点的二叉树,其节点由1至n编号,并给定一系列操作,如下:
1.rotate x,对编号x的节点进行旋转,若x为根节点,则不进行任何操作。
2.parent x,输出编号x的父亲节点编号,若x为根节点输出-1。
3.size x,输出以x为根节点的子树的节点个数。
4.balance x,判断以x为根节点的子树是否为平衡树,是则输出"yes",否则输出"no"。
输入:
输入包含多组测试用例。
每组测试用例开头为一个整数n(1<=n<=1000),代表二叉树的节点个数。
接下去n行描述,二叉树原始的状态,第i行为两个整数x,y,代表i号节点的左儿子节点为x号节点,右儿子节点为y号节点,若x或y为-1,则表示相应儿子节点不存在。编号的范围为1到n。
接下去一行为一个整数t(1<=t<=50000),代表操作的个数。
最后t行,每行代表一个对二叉树的操作,描述如上所示。
输出:
对于每组测试用例,输出操作parent x,size x和balance x的结果。
【问题解答】
一般二叉树的选择分左旋和右旋,左旋的形状是>,右旋的形状是<。
在我看来旋转的对象是最上面的那个节点,为了方便下面描述,形状中的三个节点命名为A、B、C(C可能为空树)。
1、以左旋为例说明bt_rotate_left():
(1)旋转即修改孩子指针
首先,新树根应该是B(new_root),
然后A(root)的右孩子应该指向B的左孩子(root->right = new_root->left),
最后“旋转”,A成为B的左孩子(new_root->left = root)。
经过三步就完成了左旋。
(2)维护父亲节点信息,对应上述三步修改父亲指针:
首先新树根B的父亲指向旧树根A的父亲(new_root->par = root-par),
然后B的左孩子(可能为空)的父亲应该是A(new_root->left->par = root),这里如果没有空节点作为哨兵则需保证new_root->left != NULL,
最后A的父亲是B,root->par = new_root;
(3)维护树的节点数,调整选择后的新树根B和旧树根A的size即可。
新树根的右子树不变,左子树包括原来的左子树,增加的部分是B的右子树和B本身,所以有:
new_root->size += (root->left->size + 1); 注意没有空节点作为哨兵需保证root->left != NULL
旧树根的左子树不变,右子树只包括原来的右子树(new_root)的左子树部分,所以有:
root->size -= (new_root->right->size + 1); 同样要保证root->right != NULL
2、右旋是完成对称的。
3、实现的细节包括:
1)由于需要修改前驱(父亲)节点指针,所以定义一个树根的父亲sentinel,让它的左右孩子都指向树根,
且判断“父亲的左孩子是否自己”,这样若父亲是sentinel,则一定修改的是左孩子指针。所以打印树时选择bt_show_by_tree(sentinel->left)。
2)没有空节点哨兵则需要加一些判断防止对空树进行读写parent或size,有空节点哨兵主要注意存储的节点数目很可能翻倍,建树保证翻倍的内存空间。
下面是代码的完整实现:
1 /* 2 *Author: ZhangHaiba 3 *Date: 2014-2-1 4 *File: btree_rotate.c 5 * 6 *a demo shows how to rotate binary tree while repair link relationship 7 *and parent, size information. 8 * 9 */ 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #define N 1024 15 #define CMD_LEN 128 16 17 typedef struct btree * btlink; 18 19 typedef struct btree 20 { 21 int num; 22 btlink par; 23 btlink left; 24 btlink right; 25 int size; 26 }btree; 27 28 //public 29 btlink NODE(int num, btlink par, btlink left, btlink right, int size); 30 btlink bt_create(int root_num, btlink par); 31 btlink bt_left_rotate(btlink root); 32 btlink bt_right_rotate(btlink root); 33 void bt_show_by_tree(btlink root); 34 btlink* bt_search(btlink root, int num); 35 //private 36 void tree_print(btlink root, FILE* fd); 37 38 //private for bt_create 39 int left_num[N]; //the number of left tree root 40 int right_num[N]; //the number of right tree root 41 btlink num_map_root[N]; 42 43 int main(void) 44 { 45 int n, i; 46 47 scanf("%d", &n); 48 for (i = 1; i <= n; ++i) 49 scanf("%d%d", left_num+i, right_num+i); 50 btlink sentinel = NODE(0, sentinel, NULL, NULL, -1); 51 btlink btree_a = bt_create(1, sentinel); 52 sentinel->left = sentinel->right = btree_a; 53 bt_show_by_tree(btree_a); 54 55 int op_times; 56 char cmd[CMD_LEN]; 57 int root_num; 58 59 scanf("%d", &op_times); 60 while (op_times--) { 61 scanf("%s", cmd); 62 if (strcmp(cmd, "size") == 0) { 63 scanf("%d", &root_num); 64 printf("%d\n", num_map_root[root_num]->size); 65 } else if (strcmp(cmd, "rotate") == 0) { 66 scanf("%d", &root_num); 67 btlink x = num_map_root[root_num]; 68 btlink par = x->par; 69 if (par == sentinel) //x is root 70 continue; 71 if (par->right == x) { 72 if (par->par->left == par) //left first 73 par->par->left = bt_left_rotate(par); 74 else if (par->par->right == par) 75 par->par->right = bt_left_rotate(par); 76 } else if (par->left == x) { 77 if (par->par->left == par) //left first 78 par->par->left = bt_right_rotate(par); 79 else if (par->par->right == par) 80 par->par->right = bt_right_rotate(par); 81 } 82 bt_show_by_tree(sentinel->left); //becuase left is first choice 83 } else if (strcmp(cmd, "parent") == 0) { 84 scanf("%d", &root_num); 85 btlink root = num_map_root[root_num]; 86 printf("%d\n", root->par == sentinel ? -1 : root->par->num); 87 } else if (strcmp(cmd, "balance") == 0) { 88 scanf("%d", &root_num); 89 btlink root = num_map_root[root_num]; 90 int left_size = root->left != NULL ? root->left->size : 0; 91 int right_size = root->right != NULL ? root->right->size : 0; 92 printf( abs(left_size - right_size) <= 1 ? "yes\n" : "no\n"); 93 } 94 } 95 return 0; 96 } 97 98 btlink NODE(int num, btlink par, btlink left, btlink right, int size) 99 { 100 btlink born = malloc(sizeof (btree)); 101 born->par = par; 102 born->num = num; 103 born->left = left; 104 born->right = right; 105 born->size = size; 106 return born; 107 } 108 109 btlink bt_create(int num, btlink par) //number begin with 1 110 { 111 if (num == -1) 112 return NULL; 113 btlink root = NODE(num, par, NULL, NULL, 1); 114 num_map_root[num] = root; 115 root->par = par; 116 root->left = bt_create(left_num[num], root); 117 root->right = bt_create(right_num[num], root); 118 if (root->left != NULL && root->right != NULL) 119 root->size = 1 + root->left->size + root->right->size; 120 else if (root->left != NULL) 121 root->size = 1 + root->left->size; 122 else if (root->right != NULL) 123 root->size = 1 + root->right->size; 124 return root; 125 } 126 127 btlink bt_left_rotate(btlink root) 128 { 129 btlink new_root = root->right; 130 new_root->par = root->par; //change par 131 root->right = new_root->left; 132 if (new_root->left != NULL) 133 new_root->left->par = root; //change par 134 new_root->left = root; 135 root->par = new_root; //change par 136 //modify size 137 new_root->size += root->left != NULL ? (root->left->size + 1) : 1; 138 root->size -= new_root->right != NULL ? (new_root->right->size + 1) : 1; 139 return new_root; 140 } 141 142 btlink bt_right_rotate(btlink root) 143 { 144 btlink new_root = root->left; 145 new_root->par = root->par; //change par 146 root->left = new_root->right; 147 if (new_root->right != NULL) 148 new_root->right->par = root; //change par 149 new_root->right = root; 150 root->par = new_root; //change par 151 //modify size 152 new_root->size += root->right != NULL ? (root->right->size + 1) : 1; 153 root->size -= new_root->left != NULL ? (new_root->left->size + 1) : 1; 154 return new_root; 155 } 156 157 void tree_print(btlink root, FILE *fd) 158 { 159 fprintf(fd, "("); 160 if (root != NULL) { 161 fprintf(fd, "%d", root->num); 162 tree_print(root->left, fd); 163 tree_print(root->right, fd); 164 } 165 fprintf(fd, ")"); 166 } 167 168 void bt_show_by_tree(btlink root) 169 { 170 char cmd[CMD_LEN]; 171 172 sprintf(cmd, "rm -f ./tree_src.txt"); 173 system(cmd); 174 175 FILE *fd = fopen("./tree_src.txt", "a+"); 176 fprintf(fd, "\n\t\\tree"); 177 tree_print(root, fd); 178 fprintf(fd, "\n\n"); 179 fclose(fd); 180 181 sprintf(cmd, "cat ./tree_src.txt | ~/tree/tree"); 182 system(cmd); 183 }
测试示范:
ZhangHaiba-MacBook-Pro:code apple$ ./a.out 8 2 3 4 -1 -1 6 -1 5 -1 -1 7 8 -1 -1 -1 -1 1 ____|____ | | 2 3 _|__ __|__ | | | | 4 6 _|__ ___|___ | | | | 5 7 8 _|__ _|__ _|__ | | | | | | 15 size 1 8 parent 3 1 rotate 3 3 ____|____ | | 1 6 _|__ ___|___ | | | | 2 7 8 _|__ _|__ _|__ | | | | | | 4 _|__ | | 5 _|__ | | size 1 4 size 3 8 parent 3 -1 parent 1 3 rotate 6 6 ___|____ | | 3 8 ___|___ _|__ | | | | 1 7 _|__ _|__ | | | | 2 _|__ | | 4 _|__ | | 5 _|__ | | balance 4 yes balance 1 no size 3 6 rotate 1 6 ___|____ | | 1 8 ___|___ _|__ | | | | 2 3 _|__ _|__ | | | | 4 7 _|__ _|__ | | | | 5 _|__ | | balance 1 yes size 3 2 parent 3 1 ZhangHaiba-MacBook-Pro:code apple$
最后附带jiudu OJ 1541(http://ac.jobdu.com/problem.php?pid=1541)的AC代码:
这里使用了空树作为哨兵,减少一些rotate操作中的判断次数。
/* *Author: ZhangHaiba *Date: 2014-2-1 *File: jdoj1541_btree_rotate2.c * *an AC code for jiudu oj problem 1541 *use null nodes for sentinel */ #include <stdio.h> #include <string.h> #define N 2048 //be care of null nodes #define CMD_LEN 128 typedef struct btree * btlink; typedef struct btree { int num; btlink par; btlink left; btlink right; int size; }btree; //public btlink NODE(int num, btlink par, btlink left, btlink right, int size); btlink bt_create(int root_num, btlink par); btlink bt_left_rotate(btlink root); btlink bt_right_rotate(btlink root); //private for bt_create int left_num[N]; //the number of left tree root int right_num[N]; //the number of right tree root btlink num_map_root[N]; //alloc static btree memory[N]; int cnt = 0; int main(void) { int n, i, j; while (scanf("%d", &n) != EOF) { cnt = 0; //for memory for (i = 1; i <= n; ++i) scanf("%d%d", left_num+i, right_num+i); btlink sentinel = NODE(0, sentinel, NULL, NULL, -1); btlink btree_a = bt_create(1, sentinel); sentinel->left = sentinel->right = btree_a; int op_times; char cmd[CMD_LEN]; int root_num; scanf("%d", &op_times); for (j = 0; j < op_times; ++j) { scanf("%s", cmd); if (strcmp(cmd, "size") == 0) { scanf("%d", &root_num); printf("%d\n", num_map_root[root_num]->size); } else if (strcmp(cmd, "rotate") == 0) { scanf("%d", &root_num); btlink x = num_map_root[root_num]; btlink par = x->par; if (par == sentinel) continue; if (par->right == x) { if (par->par->left == par) par->par->left = bt_left_rotate(par); else if (par->par->right == par) par->par->right = bt_left_rotate(par); } else if (par->left == x) { if (par->par->left == par) par->par->left = bt_right_rotate(par); else if (par->par->right == par) par->par->right = bt_right_rotate(par); } } else if (strcmp(cmd, "parent") == 0) { scanf("%d", &root_num); btlink root = num_map_root[root_num]; printf("%d\n", root->par == sentinel ? -1 : root->par->num); }//while(op_times--) } return 0; } btlink NODE(int num, btlink par, btlink left, btlink right, int size) { btlink born = &memory[cnt++]; born->par = par; born->num = num; born->left = left; born->right = right; born->size = size; return born; } btlink bt_create(int num, btlink par) //number begin with 1 { if (num == -1) return NODE(-1, par, NULL, NULL, 0); //null tree as sentinel end size is 0 btlink root = NODE(num, par, NULL, NULL, 1); num_map_root[num] = root; root->par = par; root->left = bt_create(left_num[num], root); root->right = bt_create(right_num[num], root); if (root->left != NULL && root->right != NULL) root->size = 1 + root->left->size + root->right->size; else if (root->left != NULL) root->size = 1 + root->left->size; else if (root->right != NULL) root->size = 1 + root->right->size; return root; } btlink bt_left_rotate(btlink root) { btlink new_root = root->right; new_root->par = root->par; //change par root->right = new_root->left; new_root->left->par = root; //change par new_root->left = root; root->par = new_root; //change par new_root->size += (root->left->size + 1); root->size -= (new_root->right->size + 1); return new_root; } btlink bt_right_rotate(btlink root) { btlink new_root = root->left; new_root->par = root->par; //change par root->left = new_root->right; new_root->right->par = root; //change par new_root->right = root; root->par = new_root; //change par new_root->size += (root->right->size + 1); root->size -= (new_root->left->size + 1); return new_root; }