二叉树经典面试题

1.求二叉树中最远的两个节点的距离,对于这样一个题经过画图分析总体来说有以下几种情况:

《二叉树经典面试题》


分析:

        通过上图我们可发现最远两个节点距离实则为某一节点左右子树的高度和,可以这样考虑,遍历树中每个节点,得出它们的左右子树高度和,从而通过比较更新得到两个节点的最远距离,这个过程是同类的子问题因此可以考虑递归算法。在递归遍历的过程中如果采用前序的话时间复杂度为O(N^2),要想进一步优化我们可以采取后序遍历,其时间复杂度为O(N)。

       我们可以定义一个全局遍历用来保存最远距离,通过传引用的方式来获取最远距离。在进行后序遍历的同时,将每一个节点左右子树的深度带回,从而得出left+right,如果当前节点的左右子树距离(它们的和)大于前一节点的左右子树距离,那么就更新这个distance.

代码实现:

template<class T>
struct BinaryTreeNode
{
	BinaryTreeNode(const T& data)
		:_data(data)
		,_left(NULL)
		,_right(NULL)
	{}

	T _data;
	BinaryTreeNode<T>* _left;
	BinaryTreeNode<T>* _right;
};

template<class T>
class BinaryTree
{
	typedef BinaryTreeNode<T> Node;

public:
	BinaryTree(const T* arr,size_t size,const T& invalid)
	{
		size_t index=0;
		_root=_CreateBinaryTree(arr,size,invalid,index);
	}
	void PreOrder()
	{
		_PreOrder(_root);
		cout<<endl;
	}
	int GetFarthestDistance()
	{
		int distance=0;
		_GetFarthestDistance(_root,distance);
		return distance;
	}
protected:
	void _PreOrder(Node* root)
	{
		if(root==NULL)
			return ;

		cout<<root->_data<<" ";
		_PreOrder(root->_left);
		_PreOrder(root->_right);
	}

	Node* _CreateBinaryTree(const T* arr,size_t size,const T& invalid,size_t& index)
	{
		assert(arr);
		Node* root=NULL;
		if(index<size && arr[index]!=invalid)
		{
			root=new Node(arr[index]);
			root->_left=_CreateBinaryTree(arr,size,invalid,++index);
			root->_right=_CreateBinaryTree(arr,size,invalid,++index);
		}
		return root;
	}

	int _GetFarthestDistance(Node* root,int& distance)
	{
		if(root==NULL)
			return 0;

		int left =_GetFarthestDistance(root->_left,distance);
		int right =_GetFarthestDistance(root->_right,distance);
		//当前节点的深度,即左右子树最大基础上+1
		int Depth=left>right ?left+1:right+1;

		if(left + right > distance)
			distance = left+right;

		return Depth;
	}
protected:
	Node* _root;
};

2.由前序遍历和中序遍历重建二叉树,例如:前序序列 1 2 3 4 5 6,中序序列 3 2 4 1 6 5

分析:前序遍历的规则是 先访问根节点–左子树–右子树,那么根据这个序列就可以判断出第一个元素1就是这               棵二叉树的根节点;

           中序遍历的规则是 先访问左子树–根节点–右子树,那么可以这样考虑先遍历一遍中序序列,先找到根节点              的位置,从而确定出左子树和右子树是哪些元素。

          先创建出根节点,然后创建左子树和右子树,此过程就可转化成子问题,递归便可完成。

《二叉树经典面试题》


代码实现:

//根据前序和中序序列重建二叉树
template<class T>
class RebuiltBinaryTree
{
	struct RebuiltBinaryTreeNode
	{
		T _data;
		RebuiltBinaryTreeNode* _left;
		RebuiltBinaryTreeNode* _right;

		RebuiltBinaryTreeNode(const T& data)
			:_data(data)
			,_left(NULL)
			,_right(NULL)
		{}
	};
	typedef RebuiltBinaryTreeNode Node;
public:
	RebuiltBinaryTree (T* PrevOrder,T* InOrder,int len)
	{
		assert(PrevOrder && InOrder);
		_CreateSubTree(_root,PrevOrder,InOrder,len);
	}

	void PreOrder()
	{
		_PreOrder(_root);
		cout<<endl;
	}
	void PostOrder()
	{
		_PostOrder(_root);
		cout<<endl;
	}
protected:
	void _PostOrder(Node* root)
	{
		if(root==NULL)
			return;

		_PostOrder(root->_left);
		_PostOrder(root->_right);
		cout<<root->_data<<" ";
	}
	void _PreOrder(Node* root)
	{
		if(root==NULL)
			return;

		cout<<root->_data<<" ";
		_PreOrder(root->_left);
		_PreOrder(root->_right);
	}
	void _CreateSubTree(Node*& root,T* PrevOrder,T* InOrder,int len)
	{
		if(len<=0)
			return;
		//根据前序序列的性质先创建树的根节点
		root=new Node(PrevOrder[0]);  
		//遍历中序序列找根节点,以确定左右子树的区间
		int index=0;
		for(;index<len;++index)
		{
			if(PrevOrder[0]==InOrder[index])
				break;
		}
		int LeftSub=index;   //左子树节点数
		int RightSub=len-index-1;  //右子树节点数

		//递归构建左右子树,注意控制区间
		_CreateSubTree(root->_left,PrevOrder+1,InOrder,LeftSub);  
		_CreateSubTree(root->_right,PrevOrder+index+1,InOrder+index+1,RightSub);
	}

protected:
	Node* _root;
};

void Test()
{
	int PrevOrder[]={1,2,3,4,5,6};
	int InOrder[]={3,2,4,1,6,5};
	RebuiltBinaryTree<int> bt(PrevOrder,InOrder,6);
	bt.PreOrder();
	bt.PostOrder();
}

3.判断一棵树是否为完全二叉树

(1)首先了解一下完全二叉树是如何定义的:

一棵二叉树至多只有最下面的一层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树。

简略画出几种:

《二叉树经典面试题》

(2)根据定义来看,我们可以采取层序遍历的方式来判定一棵二叉树到底是不是完全二叉树。

<1>如果是完全二叉树的话在层序遍历的时候只有遍历完了才会结束;如果遇到空节点,后面又有非空节点那么这就不是完全二叉树。也就是说可以这样思考,一个节点只有右孩子,或是只有左孩子但该左孩子并不是非叶子节点(对应上图不是完全二叉树的两种情况),那么就可判断该树不是完全二叉树,具体实现如下分析:

《二叉树经典面试题》


<2>将空节点也入队列,

《二叉树经典面试题》

代码实现两种方式:




4.求树中两个节点的最近公共祖先

首先这道题目没有告知我们是一棵二叉树还是普通的树,所以还得分情况考虑一下.

1.如果说明了该树为二叉树并且是一棵二叉搜索树的话,那这就很好解决了。因为二叉搜索树是经过排序了的,位于左子树的节点均小于根节点,位于右子树的节点均大于根节点,我们只需要从树的根节点开始和两个输入的节点进行比较。如果当前节点的值比两个节点都大,那么最低的公共父节点一定是在当前节点的左子树中,于是下一步遍历当前节点的左子节点;如果当前节点的值比两个节点都小,那么最低的公共父节点一定在当前节点的右子树中,下一步就遍历当前节点的右子节点;这样在树中从上到下找到的第一个在两个输入节点值之间的节点就是最低的公共父节点。

2.这棵树是一棵普通的树,又该如何求解它们的最低公共祖先?

这又分两种情况:

1>树的节点中有指向父节点的指针parent。如果树中的每个节点(除根节点外)都有一个指向父节点的指针,那么这个问题就可以转化为求两个链表的公共节点。从给定的两个结点开始,反向遍历到根节点,然后求出两个链表的第一个公共结点就是最低公共祖先。

2>是一棵普通的树且树中的节点没有指向父节点的指针parent。这有可以有两种方法,一是可以借助辅助内存,用两个链表分别保存从根节点到输入的两个节点的路径,然后将问题转化为求两个链表的最后公共节点。

《二叉树经典面试题》

这种方式为了得到从根节点开始到输入的两个节点的路径,需要遍历两次树,每遍历一次时间复杂度为O(N),得到两条路径在最差情况下时间复杂度为O(N),但是需要一定的辅助空间,所以下面再提供一种思路:

《二叉树经典面试题》



5.将二叉搜索树转换成一个排序的双向链表,要求不能创建任何新的节点,只能调整树中节点指针的指向

二叉搜索树本身就是一棵排序树,因此我们在转化成排序的双向链表时,原先指向左子节点的指针调整为链表中指向前一个节点的指针,原先指向右子节点的指针调整为链表中指向后一个节点的指针。由于要求转化之后的链表是排好序的,所以我们可以中序遍历树中的每一个节点。

具体实现如下:




    原文作者:lc_29503203
    原文地址: https://blog.csdn.net/qq_29503203/article/details/53373452
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞