写给自己看的二叉查找树:基本操作
Posted lyrich
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写给自己看的二叉查找树:基本操作相关的知识,希望对你有一定的参考价值。
搬运自我的CSDN https://blog.csdn.net/u013213111/article/details/88670399
1.定义
二叉树的每个节点有一个数据成员和两个指针,两个指针分别指向左、右子树。
1 typedef int eletype; 2 3 typedef struct node 4 { 5 eletype data; 6 struct node *lchild; 7 struct node *rchild; 8 }Tnode;
2.创建一个只含有根节点的空二叉树
注意要将根节点的左、右子树初始化为NULL。
Tnode *CreateTree(eletype data) { Tnode *root; root = malloc(sizeof(Tnode)); if (!root) printf("Create tree failed."); root->data = data; root->lchild = NULL; root->rchild = NULL; }
3.插入一个节点
通过递归来查找应该在何处插入新的节点,若新节点的data小于根节点的data,则在根节点的左子树部分插入;若新节点的data大于根节点的data,则在根节点的右子树部分插入。
第一种实现方式:Insert函数的返回值为Tnode *(Tnode型指针)。注意在递归时的语句是root->lchild = Insert(root->lchild, data);。
1 Tnode *Insert(Tnode *root, eletype data) 2 { 3 if(root == NULL) { 4 root = malloc(sizeof(Tnode)); 5 if (!root) 6 printf("Create tree failed."); 7 root->data = data; 8 root->lchild = NULL; 9 root->rchild = NULL; 10 } 11 else { 12 if (root->data > data) 13 root->lchild = Insert(root->lchild, data); 14 else 15 root->rchild = Insert(root->rchild, data); 16 } 17 18 return root; 19 }
Insert函数的返回值能不能为void呢?这就是第二种实现方式,用到二级指针来传递函数参数。递归处的语句是Insert(&((*root)->lchild), data);。
为什么要用二级指针呢?因为,新节点要插入的地方是一个原树叶节点的左子树或者右子树,而这个树叶节点的左、右子树又是NULL,直到插入新节点时才会调用malloc分配空间,如果传入的是一级指针,即使在最后一层的insert函数中完成了malloc和节点成员的初始化,但返回上一层函数后,并没有把malloc分配的空间和原树叶节点的左子树或右子树关联起来,也就是说原树叶节点的左子树或者右子树仍然是NULL。这就好比当希望调用一个函数修改某个变量的时候,不应该将该变量传值进函数,而是应该传引用,传值的话实际上被调用的函数会创建一个该变量的副本,修改的也是这个副本,而不是真正的变量。
关于二级指针的作用,这篇文章二级指针的作用详解里写得比较详细,总而言之就是:
在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。
1 void Insert(Tnode **root, eletype data) 2 { 3 if((*root) == NULL) { 4 *root = malloc(sizeof(Tnode)); 5 if (!root) 6 printf("Create tree failed."); 7 (*root)->data = data; 8 (*root)->lchild = NULL; 9 (*root)->rchild = NULL; 10 } 11 else { 12 if (data < (*root)->data) 13 Insert(&((*root)->lchild), data); 14 else 15 Insert(&((*root)->rchild), data); 16 } 17 }
4.打印
其实就是遍历,那么就有先序遍历、中序遍历和后序遍历三种方法。
注意要检测一下根节点是不是NULL。
先序遍历,根-左-右:
1 void PreOrderPrint(Tnode *root) 2 { 3 if (root == NULL) 4 return; 5 printf("%d ", root->data); 6 PreOrderPrint(root->lchild); 7 PreOrderPrint(root->rchild); 8 }
中序遍历,左-根-右:
1 void InOrderPrint(Tnode *root) 2 { 3 if (root == NULL) 4 return; 5 InOrderPrint(root->lchild); 6 printf("%d ", root->data); 7 InOrderPrint(root->rchild); 8 }
后序遍历,左-右-根:
1 void PostOrderPrint(Tnode *root) 2 { 3 if (root == NULL) 4 return; 5 PostOrderPrint(root->lchild); 6 PostOrderPrint(root->rchild); 7 printf("%d ", root->data); 8 }
5.根据data查找节点
用递归的方式实现,注意一定要记得写data == root->data
的情况。
1 Tnode *Find(Tnode *root, eletype data) 2 { 3 if (root == NULL) 4 return NULL; 5 if (data < root->data) 6 return Find(root->lchild, data); 7 if (data > root->data) 8 return Find(root->rchild, data); 9 if (data == root->data) 10 return root; 11 }
6.查找最小值或最大值
用递归或者非递归均可。
这里用递归实现查找最小值。
1 Tnode *FindMin(Tnode *root) 2 { 3 if (root == NULL) 4 return NULL; 5 else { 6 if (root->lchild == NULL) 7 return root; 8 else 9 return FindMin(root->lchild); 10 } 11 }
用非递归查找最大值,更省略的写法其实是像FindMin中一样一直使用root,而无需再定义一个tmp。
1 Tnode *FindMax(Tnode *root) 2 { 3 if (root == NULL) 4 return NULL; 5 Tnode *tmp = root; 6 while (tmp->rchild != NULL) 7 tmp = tmp->rchild; 8 return tmp; 9 }
7.根据data删除节点
用递归的方式实现,函数返回值为Tnode *(Tnode型指针),可以想象假如返回值为void的话,要用和Insert函数第二种实现方式同样的二级指针来实现。
首先要找到所要删除的节点的位置。
找到位置所在后,将所要删除的节点分为两种情况进行处理,一是有两个子树,二是只有一个子树或者没有子树,采用不同的处理方法。
对于只有一个子树的节点,将子树上移即可。这其实也包含了没有子树的情况,因为没有子树的情况下就是将NULL上移。
对于有两个子树的节点,将该节点用其右子树中最小的节点替代,然后删除右子树中最小的节点。
最后要记得free掉真正被删除的节点,注意在有两个子树的情况下,真正被删除的是其右子树中最小的节点,不断递归“删除”下去,因此free这个语句只要放在“只有一个子树或者没有子树”这种情况下即可。
1 Tnode *DeleteNode(Tnode *root, eletype data) 2 { 3 if (root == NULL) 4 return NULL; 5 if (data < root->data) 6 root->lchild = DeleteNode(root->lchild, data); 7 if (data > root->data) 8 root->rchild = DeleteNode(root->rchild, data); 9 if (data == root->data) { //Find element to be deleted 10 Tnode *tmp; 11 if (root->lchild && root->rchild) { //has two children 12 tmp = FindMin(root->rchild); //Replace with smallest in right subtree 13 root->data = tmp->data; 14 root->rchild = DeleteNode(root->rchild, tmp->data); //delete smallest in right subtree 15 } 16 else { //has one child or no child 17 tmp = root; 18 if (root->lchild == NULL) 19 root = root->rchild; 20 else if (root->rchild == NULL) 21 root = root->lchild; 22 free(tmp); 23 } 24 } 25 return root; 26 }
8.删除整棵树
用后序遍历的方式,不应该用先序遍历或者中序遍历,否则子树还未被删除时根节点就被删除了。
1 void DeleteTree(Tnode *root) 2 { 3 if (root == NULL) 4 return; 5 DeleteTree(root->lchild); 6 DeleteTree(root->rchild); 7 root->lchild = root->rchild = NULL; 8 free(root); 9 }
以上是关于写给自己看的二叉查找树:基本操作的主要内容,如果未能解决你的问题,请参考以下文章