尝试在C中创建二叉查找树,但我无法分支它

mklgxw1f  于 2023-10-16  发布在  其他
关注(0)|答案(3)|浏览(70)

每当一个新节点被插入时,它不是分支并将NewNode指向具有最大值的分支,而是将高度为2的相同节点替换为NewNode。
问题出现在第85行。
输出量:

Enter the data of first node: 10
    Enter the data of node: 7
     7
     L
    10
    Do you want to continue: 1
    Enter the data of node: 6
    6 
    L
    10
    Do you want to continue: 1
    Enter the data of node: 11
     11
     R
     10
     Do you want to continue: 0

相反,它应该以这种方式工作:

head node=10
    first node=7
    L
    temp=10  
    second node=6
    L
    L
    temp=7
    Third node=11
    R
    Temp=10

这意味着所有的变化都发生在头部的左和右节点,而不是像它应该的那样分支出来。

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

struct node
{
    int data;
    struct node *left;
    struct node *right;
}*head;

void createtrees();
void treversetrees();
void addvalue(struct node *temp,struct node *NewNode);
void searchtree(int S,struct node *temp);

void main()
{
    int S;
    createtrees();
    printf("\nData in Trees is\n");
    //treversetrees();
    printf("Enter the integer to search for: ");
    scanf("%d",&S);
    struct node *temp;
    temp=head;
    searchtree(S,temp);
}

void createtrees()
{
    struct node *NewNode,*temp;
    int data,answer=1;
    head=(struct node*)malloc(sizeof(struct node));
    
    if (head==NULL)
    {
        printf("Unable to allocate memory.");
        exit(0);
    }
    
    printf("Enter the data of first node: ");
    scanf("%d",&data);
    
    head->data=data;

    do
    {
        NewNode=(struct node*)malloc(sizeof(struct node));
        if (NewNode==NULL)
        {
            printf("Unable to allocate memory.");\
            break;
        }
        printf("Enter the data of node: ");
        scanf("%d",&data);
        printf("\n1. %d\n",data);
        
        NewNode->data=data;
        temp=head;
        
        // printf("2\n");
        if (data<temp->data)
        {
            // printf("3.01\n");
            addvalue(temp,NewNode);
            // printf("3.1\n");
        }
        else
        {
            // printf("3.02\n");
            addvalue(temp,NewNode);
            // printf("3.2\n");
        }
        printf("Do you want to continue: ");
        scanf("%d",&answer);
    }
    while(answer==1);
}

void addvalue(struct node *temp,struct node *NewNode)
{
    // printf("4\n");
    if (temp==NULL)
    {
        temp=NewNode;
    }
    else if (NewNode->data<temp->data)
    {
        printf("L\n");
        printf("\n%d\n",temp->data);
        addvalue(temp->left,NewNode);
    }
    else
    {
        printf("R\n");
        printf("\n%d\n",temp->data);
        addvalue(temp->right,NewNode);
    }
}

void searchtree(int S,struct node *temp)
{
    if (temp->data==S)
    {
        printf("%d is found in tree",S);
        return;
    }
    else if(temp->data>S)
    {
        temp=temp->right;
        searchtree(S,temp);
    }
    else if(temp->data<S)
    {
        temp=temp->left;
        searchtree(S,temp);
    }
    else if(temp==NULL)
    {
        printf("%d is not found in tree",S);
    }
    
}
iqxoj9l9

iqxoj9l91#

这里有几个函数可以让你开始:

struct node *createnode(int data)
{
    struct node *node = malloc(sizeof(*node));

    if (node == NULL)
    {
        printf("Unable to allocate memory.");
        exit(0);
    }
    node->data = data;
    node->left = NULL;
    node->right = NULL;
}
void addvalue(struct node **temp,struct node *NewNode)
{
    // printf("4\n");
    if ((*temp)==NULL)
    {
        (*temp)=NewNode;
    }
    else if (NewNode->data<(*temp)->data)
    {
        printf("L\n");
        printf("\n%d\n",(*temp)->data);
        addvalue(&(*temp)->left,NewNode);
    }
    else
    {
        printf("R\n");
        printf("\n%d\n",(*temp)->data);
        addvalue(&(*temp)->right,NewNode);
    }
}

您应该能够自己解决其余的问题,但不需要为第一个节点做任何特殊的事情。只需将head初始化为NULL,并为每个节点调用NewNode = createnode(data);addvalue(&head, NewNode);
searchtree()中有一个空指针解引用错误,但是你可以通过改变测试的顺序来修复它。

ars1skjm

ars1skjm2#

对于学习C语言的人来说,你已经做出了很大的努力。然而,在你最近发布的代码中有一些小问题,在另一个“答案”的评论中显示的链接。
1.未验证动态内存分配(malloc())是否成功。
1.代码重复(根节点没有什么特别的。)

  1. searchtree() * 在检查其值是否为NULL之前使用 * 节点指针
  2. if(Node->data>data)搜索树的错误边。
    1.程序退出而不释放分配的内存(被认为是内存泄漏)。
    1.不检查scanf()的返回值。
    我已经“重新工作”了你最近的代码,使它的功能,因为我认为你希望它的功能。
    虽然程序生成自己的数据来测试功能是很好的,但当测试没有完全测试功能时,这就不好了。
    通过从stdin加载数据,可以轻松地创建一组或多组测试数据,用于评估程序的正确性。用于测试下面代码的数据包含在一个文件中,并且很容易运行:
    ./a.out < test1.txt
4 5 1 6 6  7 9 8 3 0 2 9 9 8
9
42
7

执行结果:

Value '6' already stored
Value '9' already stored
Value '9' already stored
Value '8' already stored
Small to Large: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 ->
Large to Small: 9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 ->
Value to search: search(9)..RRRR..FOUND
Value to search: search(42)..RRRRR..not found
Value to search: search(7)..RRR..FOUND
Value to search:

数据的第一行列出了要插入到存储中的值。随后的行表示要搜索的各个值。
在开发代码时,可以简单地运行程序并输入测试数据来执行任何修改。
代码,带有一些注解:

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

// use 'typedef' to reduce verbiage
typedef struct n {
    int data;
    struct n *left;
    struct n *right;
} Node_t;

// use 'calloc' to ensure clean start
// use 'static' as func is a "helper" to other func's
// ALWAYS check for success of system calls!
static Node_t *nodecreate( int data ) {
    Node_t *np = calloc( 1, sizeof *np );
    if( !np ) {
        fprintf( stderr, "allocation failed\n" );
        exit( EXIT_FAILURE );
    }
    np->data = data;
    return np;
}

// use "void *" to make implementation opaque
// (could use "typedef void * Store;" if desired)
// NB: This function is not 'static'. It should be available to (and tested by) main().
// Challenge: Add one more node of data in main(),
// then call traverse() to verify the additional node is present.
void *addvalue( void *vp, int data ) {
    Node_t *np = vp;
    if( !np )
        return nodecreate( data );

#if 1 // change 1 to 0 to allow duplicates. Binary trees need not have unique nodes!
    if( data == np->data ) {
        printf( "Value '%d' already stored\n", data );
        return np;
    }
#endif

    if( data < np->data )
        np->left = addvalue( np->left, data );
    else
        np->right = addvalue( np->right, data );

    return np;
}

// somewhat simplified from your version
// binary search instead of costly recursion
// (could be muted, returning only true/false instead)
void search( void *vp, int data ) {
    Node_t *np = vp;
    printf( "search(%d)..", data );
    while( np ) {
        if( np->data == data ) {
            puts( "..FOUND" );
            return;
        }
        if( data < np->data ) { // comparison SAME AS when tree built. Be consistent!
            putchar( 'L' );
            np = np->left;
        } else {
            putchar( 'R' );
            np = np->right;
        }
    }
    puts( "..not found" );
}

// another "helper" function
static void traverse_S2L_wrk( Node_t *np ) {
    if( !np ) return;
    traverse_S2L_wrk( np->left );
    printf( "%d -> ", np->data );
    traverse_S2L_wrk( np->right );
}

// another "helper" function
static void traverse_L2S_wrk( Node_t *np ) {
    if( !np ) return;
    traverse_L2S_wrk( np->right );
    printf( "%d -> ", np->data );
    traverse_L2S_wrk( np->left );
}

// exposed "traverse" function (with "metadata")
void traverse( void *vp ) {
    Node_t *np = vp;
    printf( "Small to Large: " );
    traverse_S2L_wrk( np );
    putchar( '\n' );

    printf( "Large to Small: " );
    traverse_L2S_wrk( np );
    putchar( '\n' );
}

// good programs release resources before exiting
void release( void *vp ) {
    Node_t *np = vp;
    if( !np ) return;
    release( np->left );
    release( np->right );
    free( np );
}

// NB: there's nothing "special" about the root node
void *createFromTestData( void ) {
    Node_t *np = NULL;

    char buf[ 1024 ];
    fgets( buf, sizeof buf, stdin );
    for( char *cp = buf; (cp = strtok( cp, " ,;" )) != NULL; cp = NULL )
        np = addvalue( np, strtol( cp, NULL, 10 ) );
        // Challenge: Don't build a tree if there is no data supplied.
        // As it is, the above will happily create 1 node with the value 0.

    return np;
}

int main( void ) {
    void *p = createFromTestData();

    traverse( p );
    
    for( ;; ) { // perform multiple tests...
        char buf[ 64 ];
        printf( "Value to search: " );
        if( !fgets( buf, sizeof buf, stdin ) )
            break;
        search( p, strtol( buf, NULL, 10 ) );
    }

    release( p ); // free-up allocation

    return 0;
}

这段代码使用了一些你需要知道的库函数。最好花时间学习什么是可用的,而不是与问题搏斗,试图重新发明它们。一旦您真正熟练了,尝试实现各种库函数的您自己的版本可能是一种娱乐。
关于测试:最后,您可能希望添加delete( int val )以从存储中删除节点。将文件中的“测试数据”扩展为,例如:

4 5 9 4 9 2 1 5 3 7
s 7   // means 's'earch for 7
s 6
d 9   // means 'd'elete node storing 9 (if present)
d 0
a 42  // means 'a'dd node to store 42
// and so on...

测试意味着测试...
最后,作为练习,将操作树节点的函数移到一个单独的源文件中(将头文件#include中的函数原型适当地发布到main.ctree.c中)。然后,考虑用一个简单的数组来替换BST实现,该数组可以动态调整大小并按排序顺序维护。main.cheader.h和包含测试数据的各种文件不需要修改!

增编:

一旦你让一切正常工作,并决定你的树应该存储重复的数据值,这里有另一个挑战:修改代码,以便使用存储在节点中的“示例计数器”(而不是创建额外的节点)添加、显示(遍历)和删除任何重复项。

zd287kbt

zd287kbt3#

从问题的代码

struct node
{
    int data;
    struct node *left;
    struct node *right;
}*head;

void createtrees();
void treversetrees();
void addvalue(struct node *temp,struct node *NewNode);
void searchtree(int S,struct node *temp);

在许多方面,这是错误的。
返回voidvoid函数是奇怪的动物:

  • 没有参数(no arguments)
  • 无返回值(no return value)
  • 仅对一组全局数据进行操作
  • 例如,createtrees()是复数名称,但只能在单个全局树上操作
void createtrees()
{
    struct node *NewNode, *temp;
    int          data, answer = 1;
    head = (struct node*)malloc(sizeof(struct node));
  • 如果你调用createtrees()两次,它将覆盖head全局指针并泄漏内存.
  • 难调试
  • 很难重复使用,也许是不可能的
    **永远不要写交互式代码:**这是一个灾难的测试。如果你想在5个场景下测试20个元素,而程序仍然崩溃,请计算一下:数百个领域进入,小时的测试。没人想这样
    我将向您展示一种封装所有树数据的可能方法。下面是一个包含代码和参数的完整示例。我看到了二叉树的代码-和类似的集合-用我们在这个问题上看到的方式编程,可能不是一种安全的方法,所以这个例子中的代码作为一个整体可能是有用的。或者只是忽略;)

你有没有注意到代码中甚至没有Treenode不是树,因为节点不是setlinked list。像这些集合这样的数据结构是一个-可能是空的-节点集,如果模型更接近于此,则更容易编码。

关于示例

这只是个玩具为了说明,我们将使用此

typedef struct st_n
{
   int          data;
   struct st_n* left;
   struct st_n* right;
} Node;

typedef struct
{
   char* name;
   Node* root;
} Tree;

这里有几个操作:

Tree* so_create(const char* name);
Tree* so_destroy(Tree* tree);
int    so_insert(int value, Tree* tree);
int    so_traverse(Tree* tree, const char* msg);

这些函数仅在Tree和数据上运行,并按预期运行。在向用户公开的函数中没有提到node。原因是为了保护实现,这样我们就可以在不需要更改用户代码的情况下更改细节。而且用户也不能因为触碰内部指针而把事情搞砸。

与原始发布的代码相比:

  • 没有全局变量。我们可以在程序中对任意数量的Trees进行操作
  • so_create返回一个新的空树,并带有一个名称,以允许某种序列化。
  • 在下面提供的示例中,输入值被过滤到2个树的最小数组中
  • so_destroy返回一个NULL,可以用来使树指针在树被删除的同一行中无效。在复杂的代码中,它可以保存作业或时间,或两者兼而有之
  • so_insert()使用了预期的范例,将一个东西插入到一个 * 容器 * 中,这里是树。它更容易阅读
  • so_traverse使用 in_order 算法,因此数据以升序排序返回

main()示例

int main(void)
{
  const int N      = 16;  // test size
  int*      vector = vector_of_N(N);
  printf("    [0..%d in random order] Vector: ", N - 1);
  for (int i = 0; i < N; i += 1) printf("%d ", vector[i]);

  printf(
      "\n    Now creates trees with even and odd "
      "values\n");

  Tree* tree[2] = {so_create("even"), so_create("odd")};
  for (int i = 0; i < N; i += 1)
      if ((vector[i] % 2) == 0)
          so_insert(vector[i], tree[0]);
      else
          so_insert(vector[i], tree[1]);

  so_traverse(tree[0], "even values");
  so_traverse(tree[1], "odd values");
  tree[0] = so_destroy(tree[0]);
  tree[1] = so_destroy(tree[1]);
  free(vector);
  return 0;
}

关于代码

生成测试数据

一个辅助函数,vector_of_N()返回一个小的int数组进行测试:从0(N-1)的所有值按随机顺序排列。
也许你可以看到这种差异,并留在键盘发明数据在每个测试中的原始代码.
这些值是随机的,可以是示例中的16,也可以是某些测试中的几千。没什么区别。

const int N      = 16;  // test size
    int*      vector = vector_of_N(N);
    printf("    [0..%d in random order] Vector: ", N - 1);
    for (int i = 0; i < N; i += 1) printf("%d ", vector[i]);

    printf(
        "\n    Now creates trees with even and odd "
        "values\n");

这是在main和显示器的开始

[0..15 in random order] Vector: 9 14 3 0 5 8 1 7 6 12 2 4 10 11 13 15
    Now creates trees with even and odd values

创建trees

由于Tree数据的封装,很容易创建任何数量的树。让我们尝试2 ;)

Tree* tree[2] = {so_create("even"), so_create("odd")};
    for (int i = 0; i < N; i += 1)
        if ((vector[i] % 2) == 0)
            so_insert(vector[i], tree[0]);
        else
            so_insert(vector[i], tree[1]);
  • 树里面有名字就派上用场了周围没有凌乱的printf s
  • 因为so_create()返回一个指向新树的简单指针,所以它们可以在一行中创建,直接放入树数组中。
  • 上面的函数提供了我们想要的测试数据,没有交互代码。
  • for循环数据
  • 并且if在一个树中插入奇数值并且在另一个树中插入偶数值。这只是一个玩具,所以我们可以看到如何强制标准和过滤数据,可以是任何东西
so_traverse(tree[0], "even values");
    so_traverse(tree[1], "odd values");
    tree[0] = so_destroy(tree[0]);
    tree[1] = so_destroy(tree[1]);
    free(vector);

我们穿过两棵树。但我们知道会发生什么,因为数据是以随机顺序插入的,但内容是事先知道的。这提供了一些安全性。
除了这个我没有做任何测试。
然后树被破坏,包含输入数据的向量被破坏,仅此而已

此代码的输出

[0..15 in random order] Vector: 9 14 3 0 5 8 1 7 6 12 2 4 10 11 13 15
    Now creates trees with even and odd values
        [+] Insert 9 into tree "odd"
        [+] Insert 14 into tree "even"
        [+] Insert 3 into tree "odd"
        [+] Insert 0 into tree "even"
        [+] Insert 5 into tree "odd"
        [+] Insert 8 into tree "even"
        [+] Insert 1 into tree "odd"
        [+] Insert 7 into tree "odd"
        [+] Insert 6 into tree "even"
        [+] Insert 12 into tree "even"
        [+] Insert 2 into tree "even"
        [+] Insert 4 into tree "even"
        [+] Insert 10 into tree "even"
        [+] Insert 11 into tree "odd"
        [+] Insert 13 into tree "odd"
        [+] Insert 15 into tree "odd"

even values    Tree "even"
0 2 4 6 8 10 12 14
    [--]

odd values    Tree "odd"
1 3 5 7 9 11 13 15
    [--]

 Deleting 4
 Deleting 2
 Deleting 6
 Deleting 10
 Deleting 12
 Deleting 8
 Deleting 0
 Deleting 14 [at root]. "even" destroyed
 Deleting 1
 Deleting 7
 Deleting 5
 Deleting 3
 Deleting 15
 Deleting 13
 Deleting 11
 Deleting 9 [at root]. "odd" destroyed

备注:

  • 我们知道输入将是从015的随机顺序的数字。
  • 我们可以看到它们插入的顺序以及在哪棵树上,因为名称是树 * 元数据 * 的一部分。
  • 然后我们看到每棵树上的内容
  • 我们提前知道了期望值
  • so_traverse()接受可选的const char*作为消息。方便测试。
  • 在删除时,显示每个节点引用的数据,因此我们可以了解用于删除的遍历算法。
  • 根节点是最后一个被删除的节点,并且它被标记为这样。

我没有实现搜索,因为它是微不足道的。请让我知道如果它是必要的。

完整代码

我不会评论的功能,我相信他们是非常简单的,我只是试图展示一种方法来写这一点。无论如何,请写任何问题,我会写回代码和职位。
我不会说这是一个好的解决方案,甚至是一个解决方案。但它成功了--嗯,就像这样--在第一次。

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

typedef struct st_n
{
    int          data;
    struct st_n* left;
    struct st_n* right;
} Node;

typedef struct
{
    char* name;
    Node* root;
} Tree;

Tree* so_create(const char* name);
Tree* so_destroy(Tree* tree);
int   so_insert(int value, Tree* tree);
int   so_traverse(Tree* tree, const char* msg);

// Node* functions: not to be used outside
void so_destroy_nd(Node*);
int  so_insert_nd(int, Node*);
int  so_traverse_nd(Node* node);

// helper that returns a vector of random values from 0 to
// N-1
int* vector_of_N(size_t N);

int main(void)
{
    srand(230928);
    const int N      = 16;  // test size
    int*      vector = vector_of_N(N);
    printf("    [0..%d in random order] Vector: ", N - 1);
    for (int i = 0; i < N; i += 1) printf("%d ", vector[i]);

    printf(
        "\n    Now creates trees with even and odd "
        "values\n");

    Tree* tree[2] = {so_create("even"), so_create("odd")};
    for (int i = 0; i < N; i += 1)
        if ((vector[i] % 2) == 0)
            so_insert(vector[i], tree[0]);
        else
            so_insert(vector[i], tree[1]);

    so_traverse(tree[0], "\neven values");
    so_traverse(tree[1], "\nodd values");
    tree[0] = so_destroy(tree[0]);
    tree[1] = so_destroy(tree[1]);
    free(vector);
    return 0;
}

int so_traverse(Tree* tree, const char* msg)
{
    if (tree == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    printf("    Tree \"%s\"\n", tree->name);
    so_traverse_nd(tree->root);
    printf("\n    [--]\n\n");
    return 0;
}

int so_traverse_nd(Node* node)
{
    if (node == NULL) return 0;
    so_traverse_nd(node->left);
    printf("%d ", node->data);
    so_traverse_nd(node->right);
    return 0;
}

int* vector_of_N(size_t N)
{
    if (N < 2) return NULL;
    N      = N > 100 ? 100 : N;
    int* V = malloc(N * sizeof(int));
    for (size_t i = 0; i < N; i += 1) V[i] = (int)i;
    for (size_t i = 0; i < N - 1; i += 1)
    {
        int p    = 1 + (rand() % (N - i - 1));
        int temp = V[i];
        V[i]     = V[p];
        V[p]     = temp;
    }
    return V;
}

Tree* so_create(const char* name)
{
    Tree* one = malloc(sizeof(Tree));
    one->name = malloc(1 + strlen(name));
    strcpy(one->name, name);
    one->root = NULL;
    return one;
}

Tree* so_destroy(Tree* tree)
{
    if (tree == NULL) return NULL;
    if (tree->root != NULL)
    {
        so_destroy_nd(tree->root->left);
        so_destroy_nd(tree->root->right);
        printf(
            "\n Deleting %d [at root]. \"%s\" destroyed",
            tree->root->data, tree->name);
    }
    free(tree->name);
    free(tree->root);
    free(tree);
    return NULL;
}

void so_destroy_nd(Node* node)
{
    if (node == NULL) return;
    so_destroy_nd(node->left);
    so_destroy_nd(node->right);
    printf("\n Deleting %d", node->data);
    free(node);
}

int so_insert(int value, Tree* tree)
{
    if (tree == NULL) return -1;
    fprintf(
        stderr, "\t[+] Insert %d into tree \"%s\"\n", value,
        tree->name);
    if (tree->root == NULL)
    {  // was empty
        tree->root        = malloc(sizeof(Node));
        tree->root->left  = NULL;
        tree->root->right = NULL;
        tree->root->data  = value;
        return 0;
    }
    // value was at root node
    if (tree->root->data == value) return 0;
    // insert at L or R subtree
    if (tree->root->data > value)
    {
        if (tree->root->left == NULL)
        {
            Node* nd         = malloc(sizeof(Node));
            tree->root->left = nd;
            nd->data         = value;
            nd->left         = NULL;
            nd->right        = NULL;
            return 0;
        }
        else
            return so_insert_nd(value, tree->root->left);
    }
    // new value is greater than one at root
    if (tree->root->right == NULL)
    {
        Node* nd          = malloc(sizeof(Node));
        tree->root->right = nd;
        nd->data          = value;
        nd->left          = NULL;
        nd->right         = NULL;
        return 0;
    }
    else
        return so_insert_nd(value, tree->root->right);
}

int so_insert_nd(int value, Node* node)
{
    // end game
    if (node == NULL)
    {
        Node* nd  = malloc(sizeof(Node));
        nd->data  = value;
        nd->left  = NULL;
        nd->right = NULL;
        return 0;
    }
    // already here
    if (node->data == value) return 0;
    if (node->data > value)
    {
        if (node->left == NULL)
        {
            Node* nd   = malloc(sizeof(Node));
            node->left = nd;
            nd->data   = value;
            nd->left   = NULL;
            nd->right  = NULL;
            return 0;
        }
        else
            return so_insert_nd(value, node->left);
    }
    else
    {
        if (node->right == NULL)
        {
            Node* nd    = malloc(sizeof(Node));
            node->right = nd;
            nd->data    = value;
            nd->left    = NULL;
            nd->right   = NULL;
            return 0;
        }
        else
            return so_insert_nd(value, node->right);
    }
    return -1;
}

相关问题