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