Проблема в B-дереве (не B + / B *) реализована с файлами в C - PullRequest
2 голосов
/ 16 апреля 2011

Я новичок здесь и, прежде всего, я хочу извиниться, если я сделаю ошибки, о которых идет речь. Моя проблема заключается в следующем: я хочу реализовать B-дерево на C, используя файл для хранения дерева ... моя программа читает 10000 строк по 10 символов в каждой из текстового файла и сохраняет это содержимое в двоичном файле .DAT, организовано через B-дерево; тогда пользователь может искать строку.

Я использую алгоритмы "Cormen, et al. - Введение в алгоритмы (3ed)", которые кажутся правильными, понятными и функциональными. НО, моя программа просто получает ошибки времени выполнения ... как Ошибка Сегментации и Бесконечный Цикл. Я пытался отлаживать в течение 5 дней, но безуспешно! Функции B-дерева являются рекурсивными, которые я лично ненавижу ... я думаю, что именно рекурсия делает отладку такой сложной!

Мой код относительно большой, и он был разделен на 2 файла, один источник, один заголовок. Я просто опубликую здесь функции B-дерева и объявления переменных. Но если кто-то захочет увидеть полный код, я опубликую ссылку «iFile.it» (это разрешено? Извините, если нет!) ...

Большое спасибо за внимание и помощь ... извините за большой вопрос!

Ссылка на источник [не так важно, просто main ()]: http://ifile.it/n73drmc/b-tree_file.c

Ссылка на заголовок [все функции здесь]: http://ifile.it/u1fa3kp/b-tree_file.h

Текстовый файл со строками: http://ifile.it/7hu95ot/arq_5.txt

ЗАМЕЧАНИЯ о коде:

i) У функции free () очень странное поведение ... поэтому я прокомментировал их, потому что, если я их использую, моя программа вызывает ошибки даже больше!

ii) Все комментарии к коду - мои идеи, чтобы попытаться решить проблемы, и нужно учитывать, что я их пробовал.

iii) Я являюсь носителем португальского языка, поэтому функции и переменные могут иметь странные имена для носителей английского языка ... извините за это!

КОД:

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

 //defs of pre-compiler

 #ifdef __linux
    #define PAUSA "read p"
    #define CLRSCR "clear"
 #else
    #define PAUSA "Pause"
    #define CLRSCR "cls"
 #endif

 #define T 5 // minimum degree of B-tree
 #define Min (T-1)
 #define Max ((2*T)-1)
 //

 //global vars
 FILE *arqt, *arqb;

 char VAL[11];
 long int lt = 0;

 typedef struct {
    unsigned int num;
    long int pos;
    char chave[(Max+1)][11]; //chave in portuguese = key in english!
    short int folha; //folha in portuguese = leaf in english!
    long int c[(Max+2)];
 } Nod;

 Nod *raiz = NULL; //raiz in portuguese = root in english!
 //

 void b_split(Nod *x, unsigned int i, Nod *y) { //B-TREE-SPLIT-CHILD //that function split a B-tree node
Nod *z = NULL;
unsigned int j=1;

z = (Nod*)realloc(z,sizeof(Nod));

fseek(arqb,0,SEEK_END);
z->pos = ftell(arqb);

z->folha = y->folha;
z->num = Min;

for(j=1;j<=Min;j++) {strcpy(z->chave[j],y->chave[j+T]);}

if (y->folha == 0) {
    for(j=1;j<=T;j++) {z->c[j] = y->c[j+T];}

}

y->num = Min;

for(j=(x->num + 1);j<=(i+1);j--) {x->c[(j+1)] = x->c[j];}

x->c[(i+1)] = z->pos;

for(j=x->num;j<=i;j--) { strcpy(x->chave[j+1],x->chave[j]); }

strcpy(x->chave[i],y->chave[T]);

x->num = x->num + 1;

fseek(arqb,x->pos,SEEK_SET);
fwrite(x,sizeof(Nod),1,arqb);

fseek(arqb,y->pos,SEEK_SET);
fwrite(y,sizeof(Nod),1,arqb);

fseek(arqb,z->pos,SEEK_SET);
fwrite(z,sizeof(Nod),1,arqb);

//free(z);
//free(y);
}

void b_ins(Nod *x, char *val) { //B-TREE-INSERT-NONFULL //insert a key in nonfull node
unsigned int i=0;
Nod *C = NULL;

i = x->num;

if (x->folha == 1) {
    while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {
        strcpy(x->chave[(i+1)],x->chave[i]);
        i--;
    }
    strcpy(x->chave[(i+1)],val);
    x->num = x->num + 1;

    fseek(arqb,x->pos,SEEK_SET);
    fwrite(x,sizeof(Nod),1,arqb);

}

else {
    while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {i--;}
    i++;

    C = (Nod*)realloc(C,sizeof(Nod));

    fseek(arqb,x->c[i],SEEK_SET);
    fread(C,sizeof(Nod),1,arqb);

    if (C->num == Max) {
        b_split(x,i,C);

        if ( strcmp(val,x->chave[i]) > 0 ) {i++;}

    }
    fseek(arqb,x->c[i],SEEK_SET);
    fread(C,sizeof(Nod),1,arqb);

    b_ins(C,val);

    //free(C);
}

}

void insere_b(char *val) { //B-TREE-INSERT //i believe the problem is here!
Nod *S = NULL,*R = NULL;

R = (Nod*)realloc(R,sizeof(Nod));
R = raiz;

if (R->num == Max) {
    S = (Nod*)realloc(S,sizeof(Nod));

/*      fseek(arqb,0,SEEK_END);
        S->pos = ftell(arqb);
*/
    raiz = S;

    S->folha = 0;
    S->num = 0;
    S->c[1] = R->pos;

 /*      fseek(arqb,S->pos,SEEK_SET);
         fwrite(S,sizeof(Nod),1,arqb);

 */
    b_split(S,1,R);
    b_ins(S,val);

    //free(S);
}

else {b_ins(R,val);}

//free(R);
}

void busca_b(Nod *x, char *val) { //B-TREE-SEARCH //self explanatory
unsigned int i=1;
Nod *C = NULL;

while( (i <= x->num) && ( strcmp(val, x->chave[i]) > 0 ) ) {i++;}

if ( (i <= x->num) && ( strcmp(val, x->chave[i]) == 0 ) ) {printf ("\nValor encontrado!\n");}

else {
    if (x->folha == 1) {printf ("\nValor NAO encontrado!\n");}

    else {
        C = (Nod*)realloc(C,sizeof(Nod));
        fseek(arqb,x->c[i],SEEK_SET);
        fread(C,sizeof(Nod),1,arqb);

        lt++;

        busca_b(C,val);
        //free(C);
    }

}

}

void cria_b() { // cria arvore B //create the B-tree
long int i = 0;
char V[11];

raiz->folha = 1;
raiz->num = 0;
raiz->pos = 0;
for (i=1;i<=(Max+1);i++) {raiz->c[i] = -1;}

fseek(arqb,raiz->pos,SEEK_SET);
fwrite(raiz,sizeof(Nod),1,arqb);

rewind(arqt);
for (i=0;i<fSize(arqt);i++) {
    fscanf(arqt,"%s\n",V);
    insere_b(V);
}
}

Ответы [ 2 ]

2 голосов
/ 17 апреля 2011

Код немного сложен для чтения, но вот что я вижу:

R = (Nod*)realloc(R,sizeof(Nod));
R = raiz;

Вы делаете выделение памяти и сразу выбрасываете результат. Результат realloc может отличаться от исходного указателя.

Возможно, вы хотите, чтобы функция инициализировала узлы вашего дерева, чтобы сделать это более понятным - мне трудно следовать коду.

Я ожидаю увидеть указатели в каждом узле дерева, но я не вижу никаких. Я не слежу за вашей реализацией. Я бы предложил нарисовать ваше дерево на бумаге со стрелками, показывающими, что указывает на что, и рассмотреть все угловые случаи (например, первую вставку, когда дерево пусто), так как это обычно то, на чем люди могут застрять. Также прокрутите ваш код с помощью отладчика и посмотрите, будет ли он вести себя так, как вы ожидаете.

РЕДАКТИРОВАТЬ: Если все дерево построено в файле (который, глядя на это немного больше, похоже, является намерением), вам, вероятно, вообще не нужно делать никакого динамического выделения памяти.

Как правило, вы хотите:

  • Остерегайтесь переполнения, например, вы пишете в область памяти за пределами того, что доступно / было выделено, строковые функции восприимчивы к ним, убедитесь, что у вас всегда есть допустимая строка с нулевым окончанием, где это необходимо, и у вас всегда есть достаточно места для вашей строки (вот и все не дольше, чем вы можете держать).
  • Проверка значений NULL, возвращаемых из функций выделения памяти.
  • Убедитесь, что у вас нет утечек памяти, т.е. выделите и выбросите указатель, никогда не вызывая free ().
  • Убедитесь, что вы всегда звоните бесплатно с ранее выделенным указателем и не используете этот указатель на более поздней точке.
  • Кажется, есть много мест, где вы делаете realloc (), но действительно хотите malloc ().

Некоторые компиляторы C / C ++ могут выполнять дополнительные проверки во время выполнения (например, Visual Studio), что может быть полезно для выявления проблемы.

Также посмотрите на хорошие комментарии Джонатана относительно соглашений и стиля.

1 голос
/ 17 апреля 2011

Заголовочные файлы должны содержать только объявления структур, typedefs, перечислений, определений и объявлений функций. Возможно, у вас есть определения встроенных функций, но, вероятно, нет.

В вашем заголовочном файле есть полные функции, которые вряд ли будут встроены и не объявлены как встроенные. Это означает, что его нельзя использовать по назначению - предоставлять объявления файлам, используя код, определенный в соответствующем файле C.

Код нелегко прочитать - и проблема не в языке. Такие конструкции, как:

while ( (i >= 1) && (strcmp(val,x->chave[i]) < 0) ) {i--;}

не являются идиоматическими C. Обычный способ написания:

while (i >= 1 && strcmp(val, x->chave[i]) < 0)
    i--;

Существуют правила кодирования, которые требуют скобок вокруг всех тел цикла и условных выражений; если вы страдаете от одного из них, вы используете одно из нескольких соглашений о кодировании:

while (i >= 1 && strcmp(val, x->chave[i]) < 0)
{
    i--;
}

while (i >= 1 && strcmp(val, x->chave[i]) < 0) {
    i--;
}

Я сильно предпочитаю первое; есть много людей, которые предпочитают последнее.

В этом мире существуют системы, отличные от Windows и Linux.

//defs of pre-compiler
#ifdef __linux
    #define PAUSA "read p"
    #define CLRSCR "clear"
#else
    #define PAUSA "Pause"
    #define CLRSCR "cls"
#endif

Это не работает особенно разумно для моей среды - MacOS X. Я не очень заинтересован в программах, которые используют системные операторы, но я полагаю, это проблема из-за того, что я старый хрен, который не использует и IDE (хотя одна из причин, по которой я не использую IDE, заключается в том, что программы командной строки не работают в них разумно, и я в основном пишу программы командной строки, поэтому они враждебны моему обычному режиму работы).


Одна из основных ошибок производительности:

for (i = 0; i < fSize(arqt); i++)

Это вызывает функцию fSize() на каждой итерации, и функция перебирает весь файл, определяя, сколько строк находится в файле, восстанавливая позицию чтения перед возвратом. Не ясно, что вам действительно нужно посчитать количество строк - вы, вероятно, можете просто читать строки по ходу. Однако, если вам нужно посчитать строки, сделайте это только один раз.

int lines = fSize(arqt);
for (i = 0; i < lines; i++)

Есть несколько мест, где вы используете петли:

for (x = 1; x <= MAX; x++)

Обычно это неправильно в C; индексы массива начинаются с нуля, и каноническая форма для цикла:

for (x = 0; x < MAX; x++)

Стилистически вы обычно резервируете все заглавные имена для значений #define или enum, а не как обычные переменные.


В функции insere_b() у вас есть:

//B-TREE-INSERT //i believe the problem is here!
void insere_b(char *val)
{
    Nod *S = NULL, *R = NULL;

    R = (Nod*)realloc(R, sizeof(Nod));
    //R = raiz;

    if (R->num == Max)
    {
        S = (Nod*)realloc(S, sizeof(Nod));

Кто-то еще указал, что строка R = raiz; сомнительна. Вы присваиваете null R; тогда вы realloc() это. Это всегда эквивалентно malloc(). Кроме того, выделенная память не инициализируется, поэтому вы получаете случайные значения для игры. Это приводит к проблемам.

Затем вы проходите аналогичную последовательность с S (выделение памяти через realloc()), хотя на этот раз вы впоследствии инициализируете части (возможно, все) выделенной структуры.

Этого достаточно, чтобы вызвать проблемы.


Когда код вводится в b_split() в первый раз, вы сталкиваетесь с проблемой со значениями позиции. В частности, y->pos - это ноль, но также x->pos, поэтому программа хранит (записывает) данные двух узлов с одинаковым смещением, что редко является рецептом счастья. Поскольку x является корневым узлом (по крайней мере, в этом контексте - первым разделением), он должен быть в позиции 0; y и z должны находиться в разных положениях. Узел y также содержит ненулевые ключи, хотя это не имеет большого значения (за исключением аккуратности), так как значение y->num указывает, что они не используются.

Вы также никогда не используете значение ключа chave[0], AFAICT. Это немного расточительно.

...