简体   繁体   中英

Segfault while deleting key from AVL tree

treedot.h

#include <stdio.h>
#include <string.h>

static void print_tree(FILE* f, tree* root) {
    fprintf(f, "  %d;\n", root->key);
    if (root->left) {
        fprintf(f, "  %d:sw->%d:n ;\n",
                root->key,
                root->left->key);
        print_tree(f, root->left);
    } else {
        fprintf(f, "  {node[style=invis, width=.1]; il_%d; }\n", root->key);
        fprintf(f, "  %d:sw->il_%d [style=invis];\n", root->key, root->key);
    }
    
    if (root->right) {
        fprintf(f, "  %d:se->%d:n ;\n",
                root->key,
                root->right->key);
        print_tree(f, root->right);// segfault here according to backtrace
    } else {
        fprintf(f, "  {node[style=invis, width=.1]; ir_%d; }\n", root->key);
        fprintf(f, "  %d:se->ir_%d [style=invis];\n", root->key, root->key);
    }
}

static void tree2dot(tree *root, const char *filename) {
    if (!root)
        return;

    char fullname[80] = {0};
    strcpy(fullname, filename);
    strcat(fullname, ".dot");
    FILE* f = fopen(fullname, "w");
    fprintf(f, "digraph %s {\n", filename);
  
    fprintf(f, " node [shape=circle]\n");
    fprintf(f, "nodesep=0.4;\nranksep=0.5;\nsplines=polyline\n");
    print_tree(f, root);

    fprintf(f, "}\n");
    fclose(f);
}

avl.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <unistd.h>

typedef struct tree {
    int key;
    int height;
    struct tree *left, *right;
} tree;

#include "tree2dot.h"

int max(int a, int b){
    return a > b ? a : b;
}

tree *new_node(int key){
    tree *node = malloc(sizeof *node);
    node->key = key;
    node->left = node->right = NULL;
    node->height = 1;
    return node;
}

int height(tree *t)
{
    return t ? t->height : 0;
}

int bfactor(tree *t){
    return height(t->left) - height(t->right);
}

void fixheight(tree *t){
    t->height = 1 + max(height(t->left), height(t->right));
}

tree *rotateleft(tree *a){
    tree *b = a->right;
    a->right = b->left;
    b->left = a;
    fixheight(a);
    fixheight(b);
    return b;
}

tree *rotateright(tree *a){
    tree *b = a->left;
    a->left = b->right;
    b->right = a;
    fixheight(a);
    fixheight(b);
    return b;
}

tree *balance(tree *a){
    fixheight(a);
    if (bfactor(a) == -2) {
        if (bfactor(a->right) > 0) {
            a->right = rotateright(a->right);
            fprintf(stderr, "big rotate right at %d\n",a->key);
        } else
            fprintf(stderr, "rotateleft at %d\n",a->key);
        return rotateleft(a);
    }
    if (bfactor(a) == 2) {
        if (bfactor(a->left) < 0) {
            a->left = rotateleft(a->left);
            fprintf(stderr, "big rotate left at %d\n",a->key);
        } else
            fprintf(stderr, "rotateright at %d\n",a->key);
        return rotateright(a);
    }
    return a;
}


tree *insert(tree *t, int key){
    if (!t)
        return new_node(key);
    else if (t->key > key)
        t->left = insert(t->left, key);
    else
        t->right = insert(t->right, key);
    return balance(t);
}


bool is_leaf(tree *t){
    return !t->left && !t->right;
}

tree *single_child(tree *t){
    if (t->left) {
        if (t->right)
            return NULL;
        else
            return t->left;
    }
    else if (t->right)
        return t->right;
    else
        return NULL;
}

// Find the inorder successor assuming the right child exists.
tree *find_next(tree *t){
    assert(t->right);
    tree *p = t->right;
    while (p->left)
        p = p->left;
    return p;
}

tree *delete(tree *p, int key){
    assert(p);
    if (p->key == key) {
        tree *c;
        if (is_leaf(p)) {
            free(p);
            return NULL;
        } else if ((c = single_child(p))) {
            free(p);
            return c;
        } else {
            // Find the inorder successor.  Would be beneficial to alternate with
            // predecessor.
            tree *n = find_next(p);
            n->left = p->left;
            n->right = p->right;
            free(p); // balance!
            return balance(n);
        }
    } else if ((p)->key > key)
        p->left = delete(p->left, key);
    else
        p->right = delete(p->right, key);
    return balance(p);
}

void rmtree(tree *t){
    if (!t)
        return;
    rmtree(t->left);
    rmtree(t->right);
    free(t);
}

int main(void){
    int key;
    tree *root = NULL;
    int n_el = 0;

    char action;
    while (1 == scanf(" %c", &action)) {
        switch (action) {
        case 'i':
            scanf("%d", &key);
            root = insert(root, key);
            tree2dot(root, "avl");
            n_el++;
            break;
        case 'd':
            scanf("%d", &key);
            root = delete(root, key);
            tree2dot(root, "avl");
            n_el--;
            break;
        default:
            assert(false && "Unknown action");
        }
    }

    tree2dot(root, "avl");
    rmtree(root);
    return 0;
}

tree2dot.h contains code to convert to graphviz representation. avl.c is AVL tree implementation. If I delete key with two children segmentation fault happens at line 20 in tree2dot.h .
I guess it's stack overflow (avl.dot's size is about 48MiB). Key deleting is definitely implemented wrong.
But what's the error with delete from tree? How to fix it?

EDIT:

(gdb) backtrace
#0  0x00007ffff7aa6705 in new_do_write (fp=0x555555559690, 
    data=0x555555559920 "dth=.1]; ir_3; }\n  3:se->ir_3 [style=invis];\n  44:se->44:n ;\n  44;\n  44:sw->0:n ;\n  0;\n  {node[style=invis, width=.1]; il_0; }\n  0:sw->il_0 [style=invis];\n  0:se->3:n ;\n  3;\n  {node[style=invis, width"..., to_do=to_do@entry=4096) at fileops.c:441
#1  0x00007ffff7aa8509 in _IO_new_do_write (to_do=4096, data=<optimized out>, fp=<optimized out>) at fileops.c:430
#2  _IO_new_do_write (fp=<optimized out>, data=<optimized out>, to_do=4096) at fileops.c:430
#3  0x00007ffff7aa7a8f in _IO_new_file_xsputn (n=35, data=<optimized out>, f=0x555555559690) at libioP.h:839
#4  _IO_new_file_xsputn (f=0x555555559690, data=<optimized out>, n=35) at fileops.c:1204
#5  0x00007ffff7a7bb11 in _IO_vfprintf_internal (s=0x555555559690, format=0x555555556020 "  {node[style=invis, width=.1]; il_%d; }\n", 
    ap=ap@entry=0x7fffff7ff5e0) at ../libio/libioP.h:839
#6  0x00007ffff7a84534 in __fprintf (stream=<optimized out>, format=<optimized out>) at fprintf.c:32
#7  0x000055555555525f in print_tree (f=0x555555559690, root=0x555555559670) at tree2dot.h:12
#8  0x000055555555523f in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:10
#9  0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#10 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#11 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#12 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#13 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#14 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#15 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#16 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#17 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#18 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#19 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#20 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#21 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#22 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#23 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#24 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#25 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#26 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#27 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
--Type <RET> for more, q to quit, c to continue without paging--c
#28 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#29 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
...
#261965 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#261966 0x00005555555552cf in print_tree (f=0x555555559690, root=0x5555555598e0) at tree2dot.h:20
#261967 0x0000555555555447 in tree2dot (root=0x5555555598e0, filename=0x55555555618f "avl") at tree2dot.h:40
#261968 0x0000555555555b0a in main () at avl.c:185

I shouldn't create any cycles in tree, so add checks.
So replacing

n->left = p->left;
n->right = p->right;

with

if(p->left!=n)n->left = p->left;//no cycles in
else n->left = NULL;
if(p->right!=n)n->right = p->right;
else n->right = NULL;

seems to do the trick.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM