基于C语言的递归二叉树实现

在这篇文章中,我们将详细探讨一个二叉树的实现,包括节点的创建、插入、遍历、删除和销毁等基本操作。代码采用C语言编写,使用结构体定义节点,并通过指针实现树的操作。

1. 数据结构定义

首先,我们定义一个节点结构体 `Node`,它包含三个成员:

typedef struct Node {
    int data;               // 节点存储的数据
    struct Node* l_child;  // 指向左子节点的指针
    struct Node* r_child;  // 指向右子节点的指针
}*node_ptr;

这里,`data`用于存储节点的值,`l_child`和`r_child`分别指向该节点的左子节点和右子节点。

2. 节点创建

节点的创建通过 `creat_node` 函数实现:

node_ptr creat_node(int value) {
    node_ptr p = (node_ptr)malloc(sizeof(Node));
    if (p == NULL) {
        printf("node_ptr error\n");
        return NULL;
    }
    p->data = value;
    p->l_child = NULL;
    p->r_child = NULL;
    return p;
}

在这个函数中,我们使用 `malloc` 动态分配内存。如果内存分配成功,节点的 `data` 被赋值,左右子节点指针被初始化为 `NULL`。

3. 插入节点

插入节点的逻辑在 `insert_node` 函数中实现:

node_ptr insert_node(Node *& root, int value) {
    if (root == NULL) {
        root = creat_node(value);
        printf("%d 插入成功\n", value);
    }
    else if (value < root->data) {
        root->l_child = insert_node(root->l_child, value);
        printf("%d 插入成功\n", value);
    }
    else if(value > root->data) {
        root->r_child = insert_node(root->r_child, value);
        printf("%d 插入成功\n", value);
    }
    return root;
}

这个函数采用递归方式插入节点。首先检查当前根节点是否为空,如果为空,则创建新节点并赋值。如果不为空,则比较待插入的值与当前节点的值,决定向左子树或右子树插入。

4. 遍历树

我们实现了三种遍历方式:前序遍历、中序遍历和后序遍历。

前序遍历

void preorder_traversal(Node *& root) {
    if (root != NULL) {
        printf("前序遍历:%d\n", root->data);
        preorder_traversal(root->l_child);
        preorder_traversal(root->r_child);
    }
}

前序遍历的顺序是:根 -> 左 -> 右。

中序遍历

void inorder_traversal(Node *& root) {
    if (root != NULL) {
        inorder_traversal(root->l_child);
        printf("中序遍历:%d\n", root->data);
        inorder_traversal(root->r_child);
    }
}

中序遍历的顺序是:左 -> 根 -> 右。

后序遍历

void postorder_traversal(Node *& root) {
    if (root != NULL) {
        postorder_traversal(root->l_child);
        postorder_traversal(root->r_child);
        printf("后序遍历:%d\n", root->data);
    }
}

后序遍历的顺序是:左 -> 右 -> 根。

5. 删除节点

删除节点的逻辑在 `delete_node` 函数中实现:

node_ptr delete_node(Node *& root, int value) {
    if (root == NULL) {
        return root;
    }
    if (value < root->data) {
        root->l_child = delete_node(root->l_child, value);  
    } else if(value > root->data) {
        root->r_child = delete_node(root->r_child, value);
    } else {
        // 节点找到了
        if (root->l_child == NULL) {
            node_ptr temp = root->r_child;
            free(root);
            return temp;
        } else if (root->r_child == NULL) {
            node_ptr temp = root->l_child;
            free(root);
            return temp;
        }
        node_ptr temp = find_min(root->r_child);
        root->data = temp->data;
        root->r_child = delete_node(root->r_child, temp->data);
    }
    return root;
}

删除节点的步骤包括:找到要删除的节点,处理三种情况(节点没有子节点、只有一个子节点、两个子节点)。

处理具有两个非空子节点的节点:

在二叉搜索树(BST)中,当我们需要删除一个具有两个非空子节点的节点时,我们需要找到一个合适的节点来替换它,以保持二叉搜索树的性质,即对于树中的每个节点,其左子树的所有节点的值都小于该节点的值,而其右子树的所有节点的值都大于该节点的值。

选择右子树中的最小节点(后继节点)作为替换节点的原因如下:

  1. 保持顺序:后继节点是紧跟在被删除节点之后的下一个较大节点。因此,用后继节点的值替换被删除节点的值可以保持树的有序性。

  2. 简化操作:后继节点保证在其左子树中没有节点(因为它是最小的),这意味着它最多只有一个右子节点。因此,在替换之后,我们可以安全地删除后继节点,而不需要再次处理复杂的子树结构。

  3. 避免破坏树结构:如果我们选择左子树中的最大节点(前驱节点)来替换,虽然也可以保持树的性质,但在某些情况下可能会导致树的不平衡。而选择后继节点通常会导致更平衡的树结构。

具体操作步骤如下:

  • 找到要删除的节点。
  • 找到该节点的右子树中的最小节点(后继节点)。
  • 将后继节点的值复制到要删除的节点。
  • 在右子树中递归删除后继节点。由于后继节点已经被复制到了原来的位置,现在删除后继节点不会影响树的性质。

这样的操作保证了在删除节点后,整个二叉搜索树仍然保持有序,同时也简化了删除操作,因为后继节点最多只有一个子节点,使得删除后继节点变得相对简单。

6. 查找最小节点

查找最小节点的函数如下:

node_ptr find_min(Node *& root) {
    while (root->l_child != NULL) {
        root = root->l_child;
    }
    return root;
}

通过不断向左子树移动,找到最小值节点。

7. 释放树的内存

最后,我们实现了一个释放树内存的函数:

void free_tree(Node* root) {
    if (root != NULL) {
        free_tree(root->l_child);
        free_tree(root->r_child);
        free(root);
    }
}

这个函数通过递归的方式释放每个节点的内存,避免内存泄漏。

结论

通过上述实现,我们构建了一个基本的二叉树结构,支持节点的插入、删除和遍历等操作。这些基础操作为更复杂的数据结构和算法奠定了基础,理解这些基本概念对学习更高级的数据结构非常重要。希望这篇文章能帮助你更好地理解二叉树及其操作。

Lk.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
	int data;
	struct Node* l_child;
	struct Node* r_child;
}*node_ptr;
extern node_ptr creat_node(int value);
extern node_ptr insert_node(Node *& root, int value);
extern void preorder_traversal(Node *& root);
extern void inorder_traversal(Node *& root);
extern void postorder_traversal(Node *& root);
extern node_ptr delete_node(Node *& root, int value);
extern node_ptr find_min(Node *& root);
extern void free_tree(Node* root);

Lk.cpp

#include "Lk.h"

node_ptr creat_node(int value)
{
    node_ptr p = (node_ptr)malloc(sizeof(Node));
    if (p == NULL) {
        printf("node_ptr error\n");
        return NULL;
    }
    p->data = value;
    p->l_child = NULL;
    p->r_child = NULL;
    return p;
}

node_ptr insert_node(Node *& root, int value)
{
    if (root == NULL) {
        root = creat_node(value);
        printf("%d 插入成功\n", value);//每层对应的root不一样
    }
    else if (value < root->data) {
        root->l_child = insert_node(root->l_child, value);
        printf("%d 插入成功\n", value);
    }
    else if(value > root->data)
    {
        root->r_child = insert_node(root->r_child, value);
        printf("%d 插入成功\n", value);
    }
  
    return root;
}

void preorder_traversal(Node *& root)//根 左 右
{
    if (root != NULL) {
        printf("前序遍历:%d\n", root->data);
        preorder_traversal(root->l_child);
        preorder_traversal(root->r_child);
    }
}

void inorder_traversal(Node *& root)//左 根 右
{
    if (root != NULL) {
        preorder_traversal(root->l_child);
        printf("中序遍历:%d\n", root->data);
        preorder_traversal(root->r_child);
    }
}

void postorder_traversal(Node *& root)//左 右 根
{
    if (root != NULL) {
        preorder_traversal(root->l_child);
        preorder_traversal(root->r_child);
        printf("后序遍历:%d\n", root->data);
    }
}

node_ptr delete_node(Node *& root, int value)
{
    if (root == NULL)
    {
        return root;
    }
    if (value < root->data) {
        root->l_child = delete_node(root->l_child, value);  
    }else if(value > root->data){
        root->r_child = delete_node(root->r_child, value);
    }else{
        if (root->l_child == NULL) {
            node_ptr temp = root->r_child;
            free(root);
            return temp;
        }
        else if (root->r_child == NULL) {
            node_ptr temp = root->l_child;
            free(root);
            return temp;
        }
        node_ptr temp = find_min(root->r_child);
        root->data = temp->data;
        root->r_child = delete_node(root->r_child, temp->data);
    }
    return root;
}

node_ptr find_min(Node *& root)
{
    while (root->l_child!=NULL)
    {
        root = root->l_child;
    }
    return root;
}
void free_tree(Node* root) {
    if (root != NULL) {
        free_tree(root->l_child);
        free_tree(root->r_child);
        free(root);
    }
}

main.cpp

#include"Lk.h"
int main() {
	/*extern node_ptr creat_node(int value);
	extern node_ptr insert_node(Node * &root, int value);
	extern void preorder_traversal(Node * &root);
	extern void inorder_traversal(Node * &root);
	extern void postorder_traversal(Node * &root);
	extern node_ptr delete_node(Node * &root, int value);
	extern node_ptr find_min(Node * &root);
	extern void free_tree(Node * root);*/
	node_ptr root = NULL;
	for(int i=1;i<6;i++)
	insert_node(root, i);
	printf("删除前:\n");
	preorder_traversal(root);
	inorder_traversal(root);
	postorder_traversal(root);
	root = delete_node(root, 1);
	root = delete_node(root, 2);
	root = delete_node(root, 3);
	printf("删除后:\n");
	preorder_traversal(root);
	inorder_traversal(root);
	postorder_traversal(root);
	free_tree(root);
	
	return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值