关于C#:Red Black Tree插入操作不断崩溃

Red Black Tree insertion operation keeps on crashing

我正在尝试实现Red Black Tree,但我自己无法对其进行编码,因此我在搜索它是用C编写的代码,并在github上找到了下面经过分析的代码,它非常简单明了,所以我做了稍作修改(只是重命名了几个变量并添加了一个菜单),当我尝试运行它时,在尝试旋转树时遇到了一个问题。插入工作正常,直到树变得不平衡为止,特别是当叔叔变成"黑色"时,我的程序无法旋转树,并且崩溃了。因此,让我向您解释程序的流程:每当我们插入一个新节点时,都会调用一个名为insert的函数,在成功插入之后,它将调用另一个名为insertfixup的函数,该函数检查Red Black Tree的任何属性是否已经存在。如果叔叔是红色,那么它会根据问题节点的叔叔是红色还是黑色来旋转树,如果叔叔是红色,那么它可以正常工作,但是一旦叔叔变成黑色,它就会崩溃。有人可以检查一下我在下面提供的代码,并指出导致问题的确切原因吗,我怀疑它与指针有关,但无法弄清楚实际发生了什么,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

typedef struct RB_Tree {
    struct RB_Tree *left, *right, *parent;
    int info;
    char color;
} node;
int count = 0;

void left_rotate(node **root, node *par) {  
    node *child = par->right;

    par->right = child->left;

    if (child->left!=NULL)
        child->left->parent = par;

    child->parent = par->parent;

    if (par->parent == NULL)
        (*root) = child;
    else
    if (par == par->parent->left)
        par->parent->left = child;
    else
        par->parent->right = child;

    child->left = par;
    par->parent = child;
}

void right_rotate(node **root, node *par) {
    node *child = par->left;

    par->left = child->right;

    if (child->right!=NULL)
        child->right->parent = par;

    child->parent = par->parent;

    if (par->parent == NULL)
        (*root) = child;
    else
    if (par = par->parent->left)
        par->parent->left = child;
    else
        par->parent->right = child;

    child->right = par;

    par->parent = child;
}

void insertFixup(node **root, node *new) {
    while (new != *root && new->parent->color == 'R') {
        node *uncle;
        //Find uncle and store uncle in"uncle" variable!

        if (new->parent == new->parent->parent->left) {
            uncle = new->parent->parent->right;
        } else {
            uncle = new->parent->parent->left;
        }

        if (uncle == NULL || uncle->color == 'B') {

            if (new->parent == new->parent->parent->left && new == new->parent->left) {
                printf("Inside ll case before rot");
                char col = new->parent->color;
                new->parent->color = new->parent->parent->color;
                new->parent->parent->color = col;

                right_rotate(root, new->parent->parent);
            }

            if (new->parent == new->parent->parent->right && new == new->parent->right) {
                char col = new->parent->color;
                new->parent->color = new->parent->parent->color;
                new->parent->parent->color = col;
                left_rotate(root, new->parent->parent);
            }

            if (new->parent == new->parent->parent->left && new == new->parent->right) {
                char col = new->color;
                new->color = new->parent->parent->color;
                new->parent->parent->color = col;
                //printf("\
Here we are in the left right!");

                left_rotate(root, new->parent);

                right_rotate(root, new->parent->parent);
            }

            if (new->parent == new->parent->parent->right && new == new->parent->left) {
                char col = new->color;
                new->color = new->parent->parent->color;
                new->parent->parent->color = col;
                right_rotate(root, new->parent);
                left_rotate(root, new->parent->parent);
            }
        }

        if (uncle) {
            if (uncle->color == 'R') {
                uncle->color = 'B';
                new->parent->color = 'B';
                new->parent->parent->color = 'R';
                new = new->parent->parent;
            }
        }
    }

    (*root)->color = 'B';
}

void insert(node **root, int data) {
    node *new = (node*)malloc(sizeof(node));
    new->info = data;
    new->parent = new->left = new->right = NULL;

    if (*root == NULL) {
        (*root) = new;
        new->color = 'B';
    } else {
        node *par;
        node *temp = (*root);

        while (temp) {
            par = temp;
            if(new->info > temp->info)
                temp = temp->right;
            else
                temp = temp->left;
        }

        new->parent = par;

        if (par->info > new->info)
            par->left = new;
        else
            par->right = new;

        new->color = 'R';

        insertFixup(root, new);
    }
}

void main() {
    int men, c, data;
    node *root = NULL;

    while (1) {
        system("cls");
        printf("1.) Insert\
"
);
        printf("2.) exit\
"
);
        printf("Enter your choice :");
        scanf("%d", &men);
        switch (men) {
          case 1:
            printf("Enter data :");
            scanf("%d", &data);
            insert(&root, data);
            printf("%d successfully added!", data);
            break;

          case 2:
            exit(0);

          default:
            printf("Invalid choice!");
            while ((c = fgetc(stdin)) != '\
'
) {}
            break;      
        }
        getch();
    }
}


在第53行带有警告的情况下进行编译:

1
2
else if(par = par->parent->left)
    par->parent->left = child;

您正在分配,要比较:

1
2
else if(par == par->parent->left)
    par->parent->left = child;


主要问题

如前所述,第53行

else if(par = par->parent->left)

必须为

1
else if(par == par->parent->left)

insertFixup中的算法不正确,例如,先插入数据3,然后插入2,然后插入1,则会导致崩溃,我在互联网上看到一个相同的定义,可能您理解了,但不幸的是您的源已被窃听。该定义有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void insertFixup(node **root, node *new) {
  while (new != *root && new->parent->color == 'R') {
    if (new->parent == new->parent->parent->left) {
      if (new->parent->parent->right && new->parent->parent->right->color == 'R') {
        new->parent->color = 'B';
        new->parent->parent->right->color = 'B';
        new->parent->parent->color = 'R';
        new = new->parent->parent;
      }
      else {
        if (new == new->parent->right) {
          new = new->parent;
          left_rotate(root, new);
        }

        new->parent->color = 'B';
        new->parent->parent->color = 'R';
        right_rotate(root, new->parent->parent);
      }
    }
    else {
      if (new->parent->parent->left && new->parent->parent->left->color == 'R') {
        new->parent->color = 'B';
        new->parent->parent->left->color = 'B';
        new->parent->parent->color = 'R';
        new = new->parent->parent;
      }
      else {
        if (new == new->parent->left) {
          new = new->parent;
          right_rotate(root, new);
        }

        new->parent->color = 'B';
        new->parent->parent->color = 'R';
        left_rotate(root, new->parent->parent);
      }
    }
  }

  (*root)->color = 'B';
}

其他问题

* main *的签名错误,main返回一个int

如果用户未输入int,则您的scanf行160不会设置字符,并且您可以测试从未初始化的值。始终检查scanf的返回值,例如:与:

之后的代码兼容

1
2
if (scanf("%d", &men) != 1)
  men = -1;

1
2
while ((c = fgetc(stdin)) != '\
'
) {}

设置了

c但从未使用过,例如,如果达到EOF,则最糟糕的情况是因为输入已重定向到文件上,因此您将循环播放而不会结束。例如do

1
2
3
4
while ((c = fgetc(stdin)) != '\
'
)
  if (c == EOF)
    exit(0);

第10行:

1
int count = 0;

定义一个从未使用过的全局变量,您可以删除该行

要检查我的建议,我添加了该功能以显示树:

1
2
3
4
5
6
7
void display(node * x){
  if (x) {
    display(x->left);
    printf("%d", x->info);
    display(x->right);
  }
}

我修改了您的main以便能够显示树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int main()
{
  int men, c, data;
  node *root = NULL;

  while (1) {
    fputs("\
"

         "1.) Insert\
"

         "2.) exit\
"

         "3.) Display tree\
"

         "Enter your choice :",
          stdout);
    if (scanf("%d", &men) != 1)
      men = -1;

    switch (men) {
    case 1:
      printf("Enter data :");
      scanf("%d", &data);
      insert(&root, data);
      printf("%d successfully added!", data);
      break;

    case 2:
      exit(0);

    case 3:
      display(root);
      putchar('\
'
);
      break;

    default:
      puts("Invalid choice!");
      while ((c = fgetc(stdin)) != '\
'
)
        if (c == EOF)
          exit(0);
      break;      
    }
  }
}

编译和执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
bruno@bruno-XPS-8300:/tmp/t$ gcc -Wall -Wextra t.c
bruno@bruno-XPS-8300:/tmp/t$ ./a.out

1.) Insert
2.) exit
3.) Display tree
Enter your choice : 1
Enter data : 3
3 successfully added!
1.) Insert
2.) exit
3.) Display tree
Enter your choice : 1
Enter data : 2
2 successfully added!
1.) Insert
2.) exit
3.) Display tree
Enter your choice : 1
Enter data : 1
1 successfully added!
1.) Insert
2.) exit
3.) Display tree
Enter your choice : 3
1 2 3

1.) Insert
2.) exit
3.) Display tree
Enter your choice : 1
Enter data : 5
5 successfully added!
1.) Insert
2.) exit
3.) Display tree
Enter your choice : 1
Enter data : 4
4 successfully added!
1.) Insert
2.) exit
3.) Display tree
Enter your choice : 3
1 2 3 4 5

1.) Insert
2.) exit
3.) Display tree
Enter your choice : 2
bruno@bruno-XPS-8300:/tmp/t$