二叉查找树

84 阅读8分钟
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 设计一个二叉查找树的接口,为了方便对二叉查找树进行节点的增删,所以采用双向不循环链表来实现,每个节点内部都需要两个指针域,分别指向该节点的左子树(lchild)和右子树(rchild)

// 先将BST树中节点的有效键值的数据类型定为int型,用户可根据实际情况进行修改
typedef int DataType_t;

// 创建BST树结点的结构体,每个结点包含三个部分
typedef struct BSTreeNode
{
  struct BSTreeNode *lchild; // 左子树指针域 (里面存放的是左子树的地址)
  DataType_t KeyVal;         // BST树结点的键值
  struct BSTreeNode *rchild; // 右子树的指针域(里面存放的是右子树的地址)
} BSTnode_t;

// 1.创建一个带根节点的BST树,对BST树的根节点进行初始化(根节点要存有效数据)
BSTnode_t *BSTree_Create(DataType_t KeyVal)
{

  //  1.创建一个根节点,并为根节点申请内存(只有一个头节点,所以申请一块内存,申请一个结构体大小的内存,因为一个结构体就是一个结点)
  BSTnode_t *Root = (BSTnode_t *)calloc(1, sizeof(BSTnode_t)); //(这个堆内存下面存的就是这个结构体,这个结构体就是一个结点,地址下的值就是结构体类型)
  // 此时Root就是根指针

  // 记住,申请堆内存之后一定要进行判断申请的堆内存是否为空,要是空,则退出此函数,因为不做判断的话,若为空,则访问结构体里面的数据,则会出现段错误;
  if (Root == NULL)
  {
    perror("calloc memory for Root is failed");
    exit(-1); // 如果申请空间失败,则直接退出
  }

  // 下面是申请内存成功的代码
  //  2.对根节点进行初始化(根节点要存有效数据),根节点的两个指针域分别指向NULL,表示没有子树
  //  Root是结构体指针,可以访问结构体里面的元素
  Root->lchild = NULL;
  Root->KeyVal = KeyVal;
  Root->rchild = NULL;

  // 3.由于Root是是一个局部变量,函数调用完,就会释放,要是没有这个地址,则不能访问这块内存,所以将头节点的地址返回;
  return Root; // 由于返回的是指针,也可以叫地址,所以函数的返回值类型应该为BSTnode_t *(指针类型);
}

// 插入的是结点,结点也需要申请内存
//  2.创建一个新节点,并为新结点申请堆内存,以及对新节点的数据域和指针域进行初始化(初始化说白了是进行赋值动作);
BSTnode_t *BSTree_NewNode(DataType_t KeyVal) // 此时需要传参,传的是要往新结点里面存的元素(传数据域的值进来),这里的data和结构体里面的data不冲突
{
  // 2.1.创建一个新节点,并未新节点申请内存(每次只创建一个节点,所以申请一块内存,申请一个结构体大小的内存,因为一个结构体就是一个结点)
  BSTnode_t *New = (BSTnode_t *)calloc(1, sizeof(BSTnode_t)); //(这个堆内存下面存的就是这个结构体,这个结构体就是一个结点,地址下的值就是结构体类型)

  // 记住,申请堆内存之后一定要进行判断申请的堆内存是否为空,要是空,则退出此函数,因为不做判断的话,若为空,则访问结构体里面的数据,则会出现段错误;
  if (New == NULL)
  {
    perror("calloc memory for New is failed");
    return NULL; // 如果申请空间失败,则直接退出 也可以写exit(-1);
  }

  // 2.2 对新结点的数据域和指针域进行初始化 (两个指针域)
  New->lchild = NULL;
  New->KeyVal = KeyVal; // 结点中数据域的值存入传进来的值
  New->rchild = NULL;   // 存放的是当前新结点下一个结点的地址,这里是NULL,是因为只是创建了一个新节点,并没有往链表里面放

  return New; // 返回的是地址,和返回根节点的方式相似;
}

// 树中只有指定位置插入
// 向BST树中加入节点 规则:根节点的左子树的键值都是比根节点小的,根节点的右子树的键值都是比根节点大的
bool BSTree_InsertNode(BSTnode_t *Root, DataType_t KeyVal) // 往哪里树里面插,所以给此树的根节点的地址  还要告诉新节点的键值,因为里面会调用新节点的函数,需要传入键值
{
  // 注意:做遍历,肯定从根开始走,会把根的左子树或者右子树从新作为根的,所以根的地址就没有了,所以为了避免根节点地址丢失,需要对根节点地址进行备份
  BSTnode_t *Proot = Root;

  // 1.创建新节点,并对新节点进行初始化
  BSTnode_t *New = BSTree_NewNode(KeyVal);
  if (New == NULL)
  {
    perror("Create NewNode Error!\n");
    return false;
  }

  // 2.分析以下当前的BST树是否为空树,有两种情况(空树 or 非空树) (因为不能保证传过来的Root是不是空的,空也可以说是NULL)
  if (Root == NULL)
  {
    // 此时的BST树为空树,则直接把新节点作为BST树的根节点
    Root = New;
  }
  // 非空树的情况
  else
  {
    // 此时这里就是BST树为非空树(至少会有根节点),分为2种情况,键值等于根节点 or 键值不等于根节点
    // 新节点的键值等于根节点的键值

    // 此时新节点的键值不等于根节点的键值,则分为2种情况,大于 或者 小于 ;

    // 由于遍历不止一次,所以用while会好一些
    while (Proot) // 判断根节点是否为有效的,若为NULL,结束循环 //判断当前根节点有没有左子树,没有左子树,则退出循环,插入即可,有左子树,继续循环判断
    {

      // 新的节点的键值和当前新的根节点的键值做比较,如果相等,则终止函数
      if (Proot->KeyVal == New->KeyVal) //// 此时的Proot没有做偏移动作,还是指向原本的根节点
      {
        printf("Can Not Insert\n"); ////此时新节点的键值等于根节点的键值,此时无法完成插入,则直接退出
        return false;
      }
      else // 不相等的情况继续分析
      {
        // 新的节点的键值小于当前新的根节点的键值,把根的左子树作为新的根
        if (New->KeyVal < Proot->KeyVal)
        {
          if (Proot->lchild == NULL)
          {
            // 赋值
            Proot->lchild = New;
            break;
          }
          Proot = Proot->lchild; // 把根的左子树作为新的根,下面继续比较(Proot做了偏移动作)
        }
        else // 新的节点的键值大于当前新的根节点的键值,则找右子树,将右子树作为新的根
        {
          if (Proot->rchild == NULL)
          {
            // 赋值
            Proot->rchild = New;
            break;
          }
          Proot = Proot->rchild;
        }
      }
    }
  }

  return true;
}



// 4. 查找节点
BSTnode_t *BSTree_Search(BSTnode_t *Root, DataType_t KeyVal)
{
  BSTnode_t *Current = Root;

  while (Current)
  {
    if (KeyVal == Current->KeyVal)
    {
      return Current;
    }
    else if (KeyVal < Current->KeyVal)
    {
      Current = Current->lchild;
    }
    else
    {
      Current = Current->rchild;
    }
  }

  return NULL;
}

// 5. 查找最小节点
BSTnode_t *BSTree_FindMin(BSTnode_t *Root)
{
  if (Root == NULL)
    return NULL;

  while (Root->lchild)
  {
    Root = Root->lchild;
  }
  return Root;
}

// 6. 查找最大节点
BSTnode_t *BSTree_FindMax(BSTnode_t *Root)
{
  if (Root == NULL)
    return NULL;

  while (Root->rchild)
  {
    Root = Root->rchild;
  }
  return Root;
}

// 7. 删除节点
BSTnode_t *BSTree_DeleteNode(BSTnode_t *Root, DataType_t KeyVal)
{
  if (Root == NULL)
    return Root;

  // 查找要删除的节点
  if (KeyVal < Root->KeyVal)
  {
    Root->lchild = BSTree_DeleteNode(Root->lchild, KeyVal);
  }
  else if (KeyVal > Root->KeyVal)
  {
    Root->rchild = BSTree_DeleteNode(Root->rchild, KeyVal);
  }
  else
  {
    // 找到要删除的节点

    // 情况1:节点是叶子节点或只有一个子节点
    if (Root->lchild == NULL)
    {
      BSTnode_t *temp = Root->rchild;
      free(Root);
      return temp;
    }
    else if (Root->rchild == NULL)
    {
      BSTnode_t *temp = Root->lchild;
      free(Root);
      return temp;
    }

    // 情况2:节点有两个子节点
    // 找到右子树的最小节点
    BSTnode_t *temp = BSTree_FindMin(Root->rchild);
    // 复制最小节点的值
    Root->KeyVal = temp->KeyVal;
    // 删除右子树的最小节点
    Root->rchild = BSTree_DeleteNode(Root->rchild, temp->KeyVal);
  }
  return Root;
}

// 8. 前序遍历(根-左-右)
void BSTree_PreOrderTraversal(BSTnode_t *Root)
{
  if (Root == NULL)
    return;

  printf("%d ", Root->KeyVal);
  BSTree_PreOrderTraversal(Root->lchild);
  BSTree_PreOrderTraversal(Root->rchild);
}

// 9. 中序遍历(左-根-右)
void BSTree_InOrderTraversal(BSTnode_t *Root)
{
  if (Root == NULL)
    return;

  BSTree_InOrderTraversal(Root->lchild);
  printf("%d ", Root->KeyVal);
  BSTree_InOrderTraversal(Root->rchild);
}

// 10. 后序遍历(左-右-根)
void BSTree_PostOrderTraversal(BSTnode_t *Root)
{
  if (Root == NULL)
    return;

  BSTree_PostOrderTraversal(Root->lchild);
  BSTree_PostOrderTraversal(Root->rchild);
  printf("%d ", Root->KeyVal);
}

// 11. 计算树的高度
int BSTree_Height(BSTnode_t *Root)
{
  if (Root == NULL)
    return 0;

  int leftHeight = BSTree_Height(Root->lchild);
  int rightHeight = BSTree_Height(Root->rchild);

  return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1;
}

// 12. 计算节点数量
int BSTree_CountNodes(BSTnode_t *Root)
{
  if (Root == NULL)
    return 0;

  return 1 + BSTree_CountNodes(Root->lchild) + BSTree_CountNodes(Root->rchild);
}

// 13. 释放BST树
void BSTree_Free(BSTnode_t *Root)
{
  if (Root == NULL)
    return;

  BSTree_Free(Root->lchild);
  BSTree_Free(Root->rchild);
  free(Root);
}

int main()
{
  // 创建BST树
  BSTnode_t *Root = BSTree_Create(50);

  // 插入节点
  BSTree_InsertNode(Root, 30);
  BSTree_InsertNode(Root, 70);
  BSTree_InsertNode(Root, 20);
  BSTree_InsertNode(Root, 40);
  BSTree_InsertNode(Root, 60);
  BSTree_InsertNode(Root, 80);

  printf("PreOrder Traversal: ");
  BSTree_PreOrderTraversal(Root);
  printf("\n");

  printf("InOrder Traversal: ");
  BSTree_InOrderTraversal(Root);
  printf("\n");

  printf("PostOrder Traversal: ");
  BSTree_PostOrderTraversal(Root);
  printf("\n");

  // 查找节点
  BSTnode_t *found = BSTree_Search(Root, 40);
  if (found)
  {
    printf("Found node with value: %d\n", found->KeyVal);
  }
  else
  {
    printf("Node not found\n");
  }

  // 查找最小和最大节点
  BSTnode_t *min = BSTree_FindMin(Root);
  BSTnode_t *max = BSTree_FindMax(Root);
  printf("Min value: %d, Max value: %d\n", min->KeyVal, max->KeyVal);

  // 计算树的高度和节点数
  printf("Tree height: %d\n", BSTree_Height(Root));
  printf("Total nodes: %d\n", BSTree_CountNodes(Root));

  // 删除节点
  printf("Deleting node 20\n");
  Root = BSTree_DeleteNode(Root, 20);
  printf("InOrder Traversal after deletion: ");
  BSTree_InOrderTraversal(Root);
  printf("\n");

  // 释放BST树
  BSTree_Free(Root);

  return 0;
}