Bootstrap

【数据结构】二叉树的实现及遍历方法

目录

一.二叉树的声明

二.初始化

 三.创建二叉树

四.插入

五.查找

六.删除

7.二叉树的遍历

1.遍历入口

2.先序遍历

 3.中序遍历

4.后序遍历

8.完整代码


利用代码对二叉树进行实现

一.二叉树的声明

#include<iostream>
using namespace std;

#define FALSE 0
#define TRUE 1
#define Type int

//二叉树的节点类
class btNode
{
public:
	//值
	Type date;

	//左节点指针
	btNode* Left_link;

	//右节点指针
	btNode* Right_link;

	//父母节点指针
	btNode* Parent_link;
};

//二叉树类
class binaryTree
{
public:
	//根节点
	btNode* root;
};

我们需要声明两个类,一个类用来存放具体的二叉树属性,一个类用来表示一棵具体的二叉树,由根节点进行连接

二.初始化

//初始化
void Initialize(btNode& bt)
{
	bt.date = 0;

	bt.Left_link = NULL;

	bt.Right_link = NULL;

	bt.Parent_link = NULL;
}

 三.创建二叉树

//创建
int Create(binaryTree&tree)
{
	tree.root = new (btNode);

	if (tree.root == NULL)
	{
		return FALSE;
	}

	return TRUE;
}

四.插入

//插入
void Insert(btNode* root, btNode* z)
{
	btNode* new_parent=NULL;//插入节点的父节点
	
	//将要插入的节点的值与根节点的值比较,寻找合适的根节点成为插入节点的父节点
	while (root != NULL)
	{
		new_parent = root;
		//若根的值小于插入节点的值,则将根的左孩子成为新的根节点
		if (root->date < z->date)
		{
			root = root->Left_link;
		}
		//反之则将根的右孩子成为新的根节点
		else
		{
			root = root->Right_link;
		}
	}

	z->Parent_link = new_parent;
	if (new_parent == NULL)
	{
		root = z;//二叉树没有根,所以插入节点为根
	}
	//判断插入节点为左孩子还是右孩子
	else
		if (z->date < new_parent->date)
	    {
		     z = new_parent->Left_link;//值小于父节点时为左孩子
	    }
	    else
		{
		     z = new_parent->Right_link;//值大于父节点时为右孩子
	    }
}

五.查找

//查找
btNode* Search(binaryTree&tree,int in_put)
{
	//先判断根节点是否为查找的值
	if (tree.root->date == in_put)
	{
		return tree.root;
	}

	//判断根的左右孩子是否为查找的值
	if (tree.root->Left_link->date == in_put)
	{
		return tree.root->Left_link;
	}
	if (tree.root->Right_link->date == in_put)
	{
		return tree.root->Right_link;
	}
	//若都不是则进入函数进行递归继续查找
	else
	{
		btNode*x=Search(tree.root->Left_link,in_put);
		btNode*y=Search(tree.root->Right_link,in_put);
		btNode* w = NULL;//用来表示找不到的情况
		if (x != NULL)
		{
			return x;
		}
		if (y != NULL)
		{
			return y;
		}
		else
		{
			return w;//找不到则返回空指针
		}
	}
}

btNode* Search(btNode* z,int _input)
{
	//传进来的节点的左孩子
	if (z->Left_link->date == _input)
	{
		return z->Left_link;
	}
	//传进来的节点的右孩子
	if (z->Right_link->date == _input)
	{
		return z->Right_link;
	}
	//递归出口,查找到叶子节点就停止
	if (z->Left_link == NULL||z->Right_link ==NULL)
	{
		return;
	}
	//递归入口
	else
	{
		Search(z->Left_link->Left_link, _input);
		Search(z->Left_link->Right_link, _input);
		Search(z->Right_link->Left_link, _input);
		Search(z->Right_link->Right_link, _input);
	}
}

设计两个查找函数,一个进行主体查找,一个用来实现递归,但递归的查找函数不够高效,没有利用到二叉树的性质,即左孩子比右孩子值小,利用这个性质可以更高效

//优化代码,同样是递归但利用左孩子比根节点和右孩子小,右孩子比根节点和左孩子大的性质可更高效
btNode*Search1(btNode* x, int _input)
{
	if (x == NULL || _input == x->date)
	{
		return x;
	}
	if (_input < x->date)
	{
		return Search1(x->Left_link, _input);
	}
	else
	{
		return Search1(x->Right_link, _input);
	}
}

 同样是递归函数,第二个代码量比第一个减少很多

六.删除

二叉树的删除分为三种情况

1.删除的节点为叶子节点

2.删除的节点为单孩子节点

3.删除的节点为双孩子节点 

同样设计两个函数来实现删除,一个功能为删除(根据输入的值),一个功能为实现删除节点的父节点和孩子节点的交接,并讨论是哪种删除情况

//删除入口(根据值来找到要删除的节点)
btNode* Delete(binaryTree &tree,int in_put)
{
	btNode* x = Search(tree, in_put);//找到删除的节点
	btNode* y = Delete(tree.root, x);//对删除节点操作后回传进行删除
	if (y != NULL)
	{
		delete y;

		y = NULL;
	}
}

//删除
btNode* Delete(btNode* root, btNode* z)
{
	btNode* a = NULL;
	//先判断想要删除的节点是叶子节点还是普通节点(即是否有左右孩子)
	if (z->Left_link == NULL && z->Right_link == NULL)
	{
		return z;
	}
	//普通节点
	else
	{
		//判断是否为只有右孩子节点
		if (z->Left_link == NULL)
		{
			a=z->Right_link->Parent_link;//将右孩子看为a
		}
		//判断是否为只有左孩子节点
		if (z->Right_link == NULL)
		{
			a=z->Left_link->Parent_link;//将左孩子看为a
		}
		//两个孩子都有
		else
		{
			//可以采用后继节点的方法和前驱节点
			//后继节点比被删节点大,是被删节点的右子树的最小节点,即右子树的最左节点,将后继节点代替成为被删节点
			//由于后继节点比被删节点大,又是右子树的最小节点,所以肯定比被删节点的右孩子小,比被删节点的左孩子大
			//且由于是最左节点,所以该节点只能是叶子节点或单右孩子节点,不会造成同样有两个孩子
			//前驱节点也是同样的原理,是左子树的最大节点,即最右孩子
			btNode* x = NULL;
			//后继方法
			//若被删节点的右孩子为叶子节点,直接替代
			if (z->Right_link->Left_link == NULL && z->Right_link->Right_link == NULL)
			{
				//将被删节点的父节点给其右孩子
				z->Right_link->Parent_link = z->Parent_link;
				z->Right_link->Left_link = z->Left_link;
			}
			x = z->Right_link->Left_link;
			while (x!=NULL)
			{
				if (x->Left_link ==NULL&x->Right_link ==NULL||x->Left_link ==NULL&&x->Right_link ->Left_link ==NULL)
				{
					//使x代替被删节点
					x->Parent_link = z->Parent_link;
					x->Left_link = z->Left_link;
					x->Right_link = z->Right_link;
				}
				x = x->Left_link;
			}
		}
		//将删除节点的父节点移交给其孩子节点
		a->Parent_link = z->Parent_link;
		//判断删除节点是左孩子还是右孩子
		if (z->date == z->Parent_link->Left_link->date)
		{
			z->Parent_link->Left_link = a;//将a作为新的左孩子
		}
		if (z->date == z->Parent_link->Right_link->date)
		{
			z->Parent_link->Right_link = a;//将a作为新的右孩子
		}
		return z;
	}
}

7.二叉树的遍历

1.遍历入口

//遍历树
void OrderTree(binaryTree&tree)
{
	PreOrder(tree.root);
	InOrder(tree.root);
	PostOrder(tree.root);
}

2.先序遍历

先序遍历是先将根节点进行输出,先遍历左子树再遍历右子树

//先序遍历
void PreOrder(btNode* z)
{
	//递归出口
	if (z == NULL)
	{
		return;
	}
	cout << z->date << endl;

	//左在右上面在递归中会先把左子树都遍历完毕再遍历右子树
	PreOrder(z->Left_link);//先序遍历左子树
	PreOrder(z->Right_link);//先序遍历右子树
}

 3.中序遍历

//中序遍历
void InOrder(btNode* z)
{
	//递归出口
	if (z == NULL)
	{
		return;
	}

	//递归回退的过程中先打印根节点
	InOrder(z->Left_link);
	cout << z->date << endl;
	InOrder(z->Right_link);
}

4.后序遍历

//后序遍历
void PostOrder(btNode* z)
{
	if (z == NULL)
	{
		return;
	}

	//递归回退的过程中先打印孩子节点
	PostOrder(z->Left_link);
	PostOrder(z->Right_link);
	cout << z->date << endl;
}

可见用递归实现三种遍历代码都很简洁,可根据输出元素的位置来体会实现三种遍历的区别

8.完整代码

#include<iostream>
using namespace std;

#define FALSE 0
#define TRUE 1
#define Type int

//二叉树的节点类
class btNode
{
public:
	//值
	Type date;

	//左节点指针
	btNode* Left_link;

	//右节点指针
	btNode* Right_link;

	//父母节点指针
	btNode* Parent_link;
};

//二叉树类
class binaryTree
{
public:
	//根节点
	btNode* root;
};

//初始化
void Initialize(btNode& bt)
{
	bt.date = 0;

	bt.Left_link = NULL;

	bt.Right_link = NULL;

	bt.Parent_link = NULL;
}

//创建
int Create(binaryTree&tree)
{
	tree.root  = new (btNode);

	if (tree.root  == NULL)
	{
		return FALSE;
	}

	return TRUE;
}

//插入
void Insert(btNode* root, btNode* z)
{
	btNode* new_parent=NULL;//插入节点的父节点
	
	//将要插入的节点的值与根节点的值比较,寻找合适的根节点成为插入节点的父节点
	while (root != NULL)
	{
		new_parent = root;
		//若根的值小于插入节点的值,则将根的左孩子成为新的根节点
		if (root->date < z->date)
		{
			root = root->Left_link;
		}
		//反之则将根的右孩子成为新的根节点
		else
		{
			root = root->Right_link;
		}
	}

	z->Parent_link = new_parent;
	if (new_parent == NULL)
	{
		root = z;//二叉树没有根,所以插入节点为根
	}
	//判断插入节点为左孩子还是右孩子
	else
		if (z->date < new_parent->date)
	    {
		     z = new_parent->Left_link;//值小于父节点时为左孩子
	    }
	    else
		{
		     z = new_parent->Right_link;//值大于父节点时为右孩子
	    }
}

//查找
btNode* Search(binaryTree&tree,int in_put)
{
	//先判断根节点是否为查找的值
	if (tree.root->date == in_put)
	{
		return tree.root;
	}

	//判断根的左右孩子是否为查找的值
	if (tree.root->Left_link->date == in_put)
	{
		return tree.root->Left_link;
	}
	if (tree.root->Right_link->date == in_put)
	{
		return tree.root->Right_link;
	}
	//若都不是则进入函数进行递归继续查找
	else
	{
		btNode*x=Search(tree.root->Left_link,in_put);
		btNode*y=Search(tree.root->Right_link,in_put);
		btNode* w = NULL;//用来表示找不到的情况
		if (x != NULL)
		{
			return x;
		}
		if (y != NULL)
		{
			return y;
		}
		else
		{
			return w;//找不到则返回空指针
		}
	}
}

btNode* Search(btNode* z,int _input)
{
	//传进来的节点的左孩子
	if (z->Left_link->date == _input)
	{
		return z->Left_link;
	}
	//传进来的节点的右孩子
	if (z->Right_link->date == _input)
	{
		return z->Right_link;
	}
	//递归出口,查找到叶子节点就停止
	if (z->Left_link == NULL||z->Right_link ==NULL)
	{
		return;
	}
	//递归入口
	else
	{
		Search(z->Left_link->Left_link, _input);
		Search(z->Left_link->Right_link, _input);
		Search(z->Right_link->Left_link, _input);
		Search(z->Right_link->Right_link, _input);
	}
}

//优化代码,同样是递归但利用左孩子比根节点和右孩子小,右孩子比根节点和左孩子大的性质可更高效
btNode*Search1(btNode* x, int _input)
{
	if (x == NULL || _input == x->date)
	{
		return x;
	}
	if (_input < x->date)
	{
		return Search1(x->Left_link, _input);
	}
	else
	{
		return Search1(x->Right_link, _input);
	}
}

//删除
btNode* Delete(btNode* root, btNode* z)
{
	btNode* a = NULL;
	//先判断想要删除的节点是叶子节点还是普通节点(即是否有左右孩子)
	if (z->Left_link == NULL && z->Right_link == NULL)
	{
		return z;
	}
	//普通节点
	else
	{
		//判断是否为只有右孩子节点
		if (z->Left_link == NULL)
		{
			a=z->Right_link->Parent_link;//将右孩子看为a
		}
		//判断是否为只有左孩子节点
		if (z->Right_link == NULL)
		{
			a=z->Left_link->Parent_link;//将左孩子看为a
		}
		//两个孩子都有
		else
		{
			//可以采用后继节点的方法和前驱节点
			//后继节点比被删节点大,是被删节点的右子树的最小节点,即右子树的最左节点,将后继节点代替成为被删节点
			//由于后继节点比被删节点大,又是右子树的最小节点,所以肯定比被删节点的右孩子小,比被删节点的左孩子大
			//且由于是最左节点,所以该节点只能是叶子节点或单右孩子节点,不会造成同样有两个孩子
			//前驱节点也是同样的原理,是左子树的最大节点,即最右孩子
			btNode* x = NULL;
			//后继方法
			//若被删节点的右孩子为叶子节点,直接替代
			if (z->Right_link->Left_link == NULL && z->Right_link->Right_link == NULL)
			{
				//将被删节点的父节点给其右孩子
				z->Right_link->Parent_link = z->Parent_link;
				z->Right_link->Left_link = z->Left_link;
			}
			x = z->Right_link->Left_link;
			while (x!=NULL)
			{
				if (x->Left_link ==NULL&x->Right_link ==NULL||x->Left_link ==NULL&&x->Right_link ->Left_link ==NULL)
				{
					//使x代替被删节点
					x->Parent_link = z->Parent_link;
					x->Left_link = z->Left_link;
					x->Right_link = z->Right_link;
				}
				x = x->Left_link;
			}
		}
		//将删除节点的父节点移交给其孩子节点
		a->Parent_link = z->Parent_link;
		//判断删除节点是左孩子还是右孩子
		if (z->date == z->Parent_link->Left_link->date)
		{
			z->Parent_link->Left_link = a;//将a作为新的左孩子
		}
		if (z->date == z->Parent_link->Right_link->date)
		{
			z->Parent_link->Right_link = a;//将a作为新的右孩子
		}
		return z;
	}
}

//删除入口(根据值来找到要删除的节点)
btNode* Delete(binaryTree &tree,int in_put)
{
	btNode* x = Search(tree, in_put);//找到删除的节点
	btNode* y = Delete(tree.root, x);//对删除节点操作后回传进行删除
	if (y != NULL)
	{
		delete y;

		y = NULL;
	}
}

;