【数据结构】二叉树的基本操作与相关面试题

        二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

下面为大家总结了二叉树相关的性质以及面试题:

二叉树中有着一些非常重要的推论:

1. 度为2的结点比度为0的结点少1个;

2. 具有n个结点的完全二叉树的深度k 为log2(n+1)个结点;

3. 若规定根结点的层数为1,则非空二叉树第i层上最多有2^(i-1)个结点;

4. 若规定只有根结点的二叉树深度为1,则深度为k的二叉树最多有2^k-1个结点;

5. 若对于具有n个结点的完全二叉树,从上至下,从左至右排序依次从0开始,那么对于序号i:

   (1)若i>0序号为i的结点的双亲结点序号为(i-1)/2;

   (2)若2*i+1<n,则序号为i的结点左孩子结点序号为2*i+1;若2*i+1>=n,则无左孩子;

   (3)若2*i+1<n,则序号为i的结点右孩子结点序号为2*i+2;若2*i+1>=n,则无右孩子;

二叉树的结构:

template<class T>
struct BinTreeNode
{
	BinTreeNode(const T& data)
	:_pLeft(NULL)
	,_pRight(NULL)
	,_data(data)
	{}
	BinTreeNode<T>* _pLeft;
	BinTreeNode<T>* _pRight;
	T _data;
};

创建二叉树:

void _CreateBinTree(pNode& pRoot, const T* arr, size_t size, size_t& index, const T& invalid)
	{
//index要传引用,否则函数调用完,index变回原来的值,不能引用常量
		if (index < size && invalid != arr[index])//不能越界
		{
			pRoot = new Node(arr[index]);//创建根节点
			_CreateBinTree(pRoot->_pLeft, arr, size, ++index, invalid);//创建左子树
			_CreateBinTree(pRoot->_pRight, arr, size, ++index, invalid);//创建右子树
		}
	}

二叉树的遍历:

二叉树由左子树+根+右子树构成,二叉树的遍历可以分为四种:前序,中序,后序层序遍历四种。

前序遍历:根+左子树+右子树,即:先访问根,在访问左子树,在访问右子树;

中序遍历:左子树+根+右子树,即:先访问左子树,在访问根,在访问右子树;

后序遍历:左子树+右子树+根,即:先访问左子树,在访问右子树,在访问根;

层序遍历:按层的顺序,一层一层的遍历。

void _PreOrder(pNode pRoot)//前序
{
	if (pRoot)
	{
		cout << pRoot->_data << " ";
		_PreOrder(pRoot->_pLeft);
		_PreOrder(pRoot->_pRight);
	}
}
void _InOrder(pNode pRoot)//中序
{
	if (pRoot)
	{
		_InOrder(pRoot->_pLeft);
		cout << pRoot->_data << " ";
		_InOrder(pRoot->_pRight);
	}
}
void _PostOrder(pNode pRoot)//后序
{
	if (pRoot)
	{
		_PostOrder(pRoot->_pLeft);
		_PostOrder(pRoot->_pRight);
		cout << pRoot->_data << " ";
	}
}
void _LevelOrder(pNode pRoot)//层序:层序的实现较为复杂,需要借助到队列,创建队列,将根结点入队,取队头元素
{                            //访问队头元素并出队列,如果左子树不为空,则保存左子树结点,保存右子树结点。
	if (NULL == pRoot)
		return;
	queue<pNode> q;
	q.push(pRoot);
	while (!q.empty())
	{
		pNode pCur = q.front();
		cout << pCur->_data << " ";
		q.pop();
 
		if (pCur->_pLeft)
			q.push(pCur->_pLeft);
		if (pCur->_pRight)
			q.push(pCur->_pRight);
	}
}

拷贝二叉树:

//拷贝二叉树
	pNode _CopyBinTree(pNode pRoot)
	{
		pNode pNewRoot = NULL;
		if (pRoot)
		{
			pNewRoot = new Node(pRoot->_data);//
			if (pRoot->_pLeft)
				pNewRoot->_pLeft = _CopyBinTree(pRoot->_pLeft);//拷贝左子树
			if (pRoot->_pRight)
				pNewRoot->_pRight = _CopyBinTree(pRoot->_pRight);//拷贝右子树  
		}
		return pNewRoot;
	}

销毁二叉树:

//销毁二叉树
	void _DestroyBinTree(pNode pRoot)
	{
		//采用后序遍历的思想,否则 先销毁根节点,左右字数将找不到了
		if (pRoot)
		{
			_DestroyBinTree(pRoot->_pLeft);//销毁左子树
			_DestroyBinTree(pRoot->_pRight);//销毁右子树
			delete pRoot;
			pRoot = NULL;
		}
	}

层序遍历二叉树:

//层序遍历二叉树
	void _levelOrder(pNode pRoot)
	{
		if (NULL == pRoot)
			return;
		queue<pNode> q;
		q.push(pRoot);
		while (!q.empty())
		{
			pNode pCur = q.front();
			cout << pCur->_data << "";
			q.pop();
			if (pCur->_pLeft);
			   q.push(pCur->_pLeft);
			if (pCur->_pRight)
				q.push(pCur->_pRight);
		}
	}

二叉树节点的个数:

//二叉树节点的个数
	size_t size(pNode pRoot)
	{
		//左子树 + 右子树 + 1
		if (pRoot == NULL)
			return 0;
		return _size(pRoot->_pLeft) + _size(pRoot->pRight) + 1;
	}

二叉树叶子节点的个数:

//二叉树叶子节点的个数
	size_t _GetLeafCount(pNode pRoot)
	{//左子树叶子节点个数 + 右子树节点个数
		if (pRoot == NULL)
			return;
		if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
			return 1;
		return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
	}

二叉树K层节点个数:

//二叉树的第K层节点个数
	size_t _GetLevelCount(pNode pRoot, size_t K)
	{
		if (K == 0 || pRoot == NULL)//size_t是大于等于0的,不用考虑小于0
			return 0;
		if (K == 1)
			return 1;
		return _GetLevelCount(pRoot->_pLeft, K - 1) + _GetLevelCount(pRoot->_pRight, K - 1);
	}

二叉树的高度(深度):

//二叉树的高度(深度)
	size_t _Height(pNode pRoot)
	{
		//返回MAX边 + 1
		if (pRoot == NULL)
			return 0;
		if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
			return 1;
		size_t	LeftNode = _Height(pRoot->_pLeft);

		return _Height(pRoot->_pRight) > LeftNode ? _Height(pRoot->_pRight) + 1 : LeftNode + 1;
	}

二叉树查找值为data的节点:

//二叉树查找值为data的节点
	void _Find(pNode pRoot, const T& data)
	{
		if (NULL == pRoot)
			return NULL;
		if (pRoot->_data == data)
			return pRoot;
		pNodeb tmp = _Find(pRoot->_pLeft, data)//先在根找,根没有再去左子树找,找到直接返回,没找到再去右子树
			return(NULL == tmp) ? _Find(pRoot->_pRight, data) : tmp;
	}

二叉树找双亲节点:

//二叉树找双亲节点
	pNode _Parent(pNode pRoot, pNode pNode)
	{
		if (NULL == pRoot || pRoot == PNode || PNode == NULL)
			return NULL;
		if (pRoot->_pLeft == PNode || pRoot->_pRight == PNode)
			return pRoot;

		pNode tmp = _Parent(pRoot->_pLeft, PNode);
		return (NULL == tmp) ? _Parent(pRoot->_pRight, PNode) : tmp;
	}

二叉树的镜像:

//二叉树的镜像
	void _Mirror(pNode pRoot)//镜像  
	{
		if (NULL == pRoot)
			return;
		if (pRoot->_pLeft || pRoot->_pRight)
			swap(pRoot->_pLeft, pRoot->_pRight);
		_Mirror(pRoot->_pLeft);
		_Mirror(pRoot->_pRight);
	}

判断是否为完全二叉树:

bool IsCompleteBinTree()//判断是否为完全二叉树  
	{
		if (NULL == _pRoot)
			return true;
		queue<pNode> q;
		q.push(_pRoot);
		bool flag = false;
		pNode pCur = NULL;
		while (!q.empty())
		{
			pCur = q.front();//分为四种,当左右都不存在,保存下来,当左存在右不存在,将flag改为true,再判断  
							 //当左不存在右存在,一定不是完全二叉树,当左右都不存在,改为true,继续判断  
			if (flag && (pCur->_pLeft || pCur->_pRight))
			{
				return false;//当pCur为不饱和结点的时候,flag为true,此时pCur若左孩子或右孩子都不是完全二叉树  
			}
			else
			{
				if (pCur->_pLeft && pCur->_pRight)
				{
					q.push(pCur->_pLeft);
					q.push(pCur->_pRight);
				}
				else if (pCur->_pLeft)//不饱和结点  
				{
					q.push(pCur->_pLeft);
					flag = true;
				}
				else if (pCur->_pRight)
					return false;
				else//不饱和结点  
					flag = true;
			}
			q.pop();
		}
		return true;
	}

     以上就是二叉树的基本操作,里面有的部分代码借助了栈队列以及模板的相关知识,由于本人的水平有限,有不足的地方欢迎大家批评指正!

猜你喜欢

转载自blog.csdn.net/yaotengjian/article/details/81103563