写给自己看的二叉查找树(1):基本操作

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值