二叉查找树

二叉查找树又称二叉搜索树,二叉排序树。

1、二叉搜索树的原理

二叉搜索树,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NIL的结点,而叶子结点的孩子结点指针也为NIL。
2、二叉搜索树的特点:
1、每个数据都是具有唯一的一个关键字。(不能重复)
2、比根节点大的关键字,放在右边,小的放在左边。(即:左边的都小于根,右边都大于根)
3、二叉树的任何子树都是二叉树。
3、二叉树功能实现
1.查找:比键值来查找数据。
2.插入
在这里插入图片描述

3.遍历:遍历采用中序遍历
4.删除:(用创建新结点的方式删除)
删除顶替方法:
1、从删除结点的左子树中找最右边的进行顶替。
2、从删除结点的右子树中找最左边的进行顶替。
删除类型:
(1)叶子节点:直接删除,不影响原树。
(2)仅仅有左或右子树的节点:节点删除后,将它的左子树或右子树整个移动到删除节点的位置就可以。
(3)既有左又有右子树的节点:定义两个移动指针,一个记录,一个移动,先找到需要删除的结点,然后再选择删除方式(这里用第一种删除方式),找到替代的结点,然后再创建一个和替代结点一样的结点,去进行连接,再释放删除的结点,删除时,还要对数进行调,当这个替代结点存在左子树时(找到是最右的数,所以不存在右子树),要进行重复以上的操作,需要删除2次结点(一个是指定删除的,另一个是顶替的)
删除前
后

4、代码实现

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
//每一结点的数据
typedef struct _Data
{
	int first;
	char str[20];
}Data;
//二叉树结点
typedef struct _TreeNode
{
	Data data;
	struct _TreeNode*LChild;
	struct _TreeNode*RChild;

}Node;
//封装一个二叉搜索树
typedef struct _SeekTree
{
	Node* root;
	int treeSize;
}SeekTree;
//创建结点
Node* createNode(Data data)
{
	Node* newNode = (Node*)malloc(sizeof(Node));
	assert(newNode != NULL);
	newNode->data = data;
	newNode->LChild = NULL;
	newNode->RChild = NULL;
	return newNode;
}
//创建搜索树
SeekTree* createSeekTree()
{
	SeekTree* newTree = (SeekTree*)malloc(sizeof(SeekTree));
	assert(newTree != NULL);
	newTree->root = NULL;
	newTree->treeSize = 0;
	return newTree;
}
//打印
void print(Node* tree)
{
	printf("%d\t%s\n", tree->data.first,tree->data .str);
}
//递归遍历
void CurTree(Node* tree)
{
	if (tree != NULL)
	{
		CurTree(tree->LChild);
		print(tree);
		CurTree(tree->RChild);
	}
}
//插入
void insertNode(SeekTree* tree, Data data)
{
	Node* newNode = createNode(data);
	//找合适位置
	Node* pMove = tree->root;
	Node* pMoveParent = NULL;//记录
		//判断位置
		while (pMove != NULL)
		{
			pMoveParent = pMove;
			//记录移动结点的父节点
			if (data.first < pMove->data.first)
			{
				//如果小于根节点往左
				pMove = pMove->LChild;
			}
			else if (data.first > pMove->data.first)
			{
				//大于往右走
				pMove = pMove->RChild;
			}
			else//相同的处理在于你 的选择方式
			{
				strcpy(pMove->data.str, data.str);
				return;
			}
		}
		//退出循环
		if (tree->root == NULL)
		{
			tree->root = newNode;
		}
		else
		{
			//pmoveParent
			if (pMoveParent->data.first > data.first)
			{
				pMoveParent->LChild = newNode;
			}
			else 
			{
				pMoveParent->RChild = newNode;
			}
		}
	tree->treeSize++;
}
//查找
Node* search(SeekTree* tree, int first)
{
	Node* pMove = tree->root;
	if (pMove == NULL)
	{
		return pMove;
	}
	else
	{
		while (pMove->data.first != first)
		{
			if (pMove->data.first > first)
			{
				pMove = pMove->LChild;
			}
			else
			{
				pMove = pMove->RChild;
			}
			if (pMove == NULL)
			{
				return pMove;
			}
			return pMove;
		}
	}
}
//二叉树的删除
void erase(SeekTree* tree, int first)
{
	Node*pMove = tree->root;   //移动结点 
	Node*pMoveParent = NULL;   //移动结点的父节点
	//找结点,找到位置(移动到要删除的位置)
	while (pMove != NULL&&pMove->data.first != first)
	{
		pMoveParent = pMove;
		if (first < pMove->data.first)
		{
			pMove = pMove->LChild;
		}
		else if (first>pMove->data.first)
		{
			pMove = pMove->RChild;
		}
		else
		{
			break;
		}
	}
	//pMoveParent 这个位置的父节点,即  删除结点的父节点0
	// 删除结点的左右子树都存在
		if (pMove->LChild != NULL&&pMove->RChild != NULL)
		{
			//从删除节点的左子树中找最右
			Node* moveNode = pMove->LChild; //往左边走
			Node* moveNodeParent = pMove;   //记录移动的父节点
			while (moveNode->RChild != NULL)
			{
				moveNodeParent = moveNode;
				moveNode = moveNode->RChild;
			}
			//找到最右边的结点
			//创建一个元素和找到的结点一样的结点,将它放到删除结点位置
			Node* newNode = createNode(moveNode->data);
			//将找到结点,将要删除结点的左右子树连上(先连左右子树再连父节点)
			newNode->LChild = pMove->LChild;
			newNode->RChild = pMove->RChild;
			//分类讨论
			if (pMoveParent == NULL)
			{
				//为空表示要删除的是第一个根结点
				tree->root = newNode;
			}
			else if (pMove == pMoveParent->LChild)
			{
				//再判断,连的是左还是右
				pMoveParent->LChild = newNode;
			}
			else
			{
				pMoveParent->RChild = newNode;
			}
			//调整二叉树
			//调整删除的指针
			if (moveNodeParent == pMove)// moveNodeParent = pMove;
			{ 
				//位置没有改变,没有右边,删除结点只有一个左子树
				pMoveParent = newNode;
			}
			else
			{
				pMoveParent = moveNodeParent;
			}
			free(pMove);//删除指定删除结点
			pMove = moveNode;
		}
		Node* sNode = NULL;
		//如果删除的结点左右存在结点,保存删除结点的下一个结点
		if (pMove->LChild!= NULL)
		{
			//防止左边有结点
			sNode = pMove->LChild;
		}
		else if(pMove->RChild != NULL)
		{
			sNode = pMove->RChild;
		}



		//如果删除的是根结点
		if (tree->root == pMove)
		{
			tree->root = sNode;//置空,根节点的孩子结点顶上去
		}
		else
		{
			//如果是单边的,直接连上即可。
			if (pMove == pMoveParent->LChild)
			{
				pMoveParent->LChild = sNode;
			}
			else
			{
				pMoveParent->RChild = sNode;
			}
		}
		free(pMove);//如果是存在左右子树的情况,释放的就是替代那个结点
		tree->treeSize--;
	}

int main()
{

	SeekTree* tree = createSeekTree();
	Data A[5] = { 23, "wda", 12, "dasd", 56, "fger", 43, "dahgdy" };

	insertNode(tree, A[0]);
	insertNode(tree, A[1]);
	insertNode(tree, A[2]);
	insertNode(tree, A[3]);
	printf("中序遍历\n");
	CurTree(tree->root);
	Node* F = search(tree, 43);
	puts("\n");
	print(F);
	erase(tree, 56);
	printf("中序遍历\n");
	CurTree(tree->root);

	while (1);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值