二叉树的旋转操作和平衡判断

  #返回上一级

@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;
}

 

  #返回上一级

posted @ 2014-02-02 11:16  张海拔  阅读(4253)  评论(0编辑  收藏  举报