二叉树的简单面试题汇总

本文汇总了二叉树的常见面试题,包括前中后序遍历(递归与非递归)、层序遍历、求二叉树高度、叶子节点个数、第K层节点个数、判断节点存在、最近公共祖先、平衡二叉树判断、最远节点距离、重建二叉树、完全二叉树判断、二叉树镜像以及二叉搜索树转双向链表等知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.前中后序遍历(递归与非递归)

1.前序遍历

1.递归

2.非递归

2.中序遍历

1.递归

2.非递归

3.后序遍历

1.递归

2.非递归

2.层序遍历

3.求二叉树高度(深度)

4.求叶子节点个数

5.求二叉树第K层节点个数

1.求二叉树第K层节点个数(递归)

2.非递归实现求二叉树第k层的节点数

6.判断一个节点是否在一棵二叉树上

7.求两个节点的最近公共祖先

1.搜索树

2.三叉链

3.一般二叉树

8.判断一棵二叉树是否是平衡二叉树

9.求二叉树中最远两个节点的距离

10. 由前序遍历和中序遍历重建二叉树

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

12.求二叉树的镜像(即左右子树交换)

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


1.前中后序遍历(递归与非递归)

1.前序遍历

1.递归

void preOrder1(BinTree *root)     
{
	if (root != NULL)
	{
		cout << root->data << " ";
		preOrder1(root->lchild);
		preOrder1(root->rchild);
	}
}

2.非递归

对于任一结点P:

     1)访问结点P,并将结点P入栈;

     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

     3)直到P为NULL并且栈为空,则遍历结束。

void preOrder2(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *p = root;
	while (p != NULL || !s.empty())
	{
		while (p != NULL)
		{
			cout << p->data << " ";
			s.push(p);
			p = p->lchild;
		}
		if (!s.empty())
		{
			p = s.top();
			s.pop();
			p = p->rchild;
		}
	}
}

2.中序遍历

1.递归

void inOrder1(BinTree *root)     
{
	if (root != NULL)
	{
		inOrder1(root->lchild);
		cout << root->data << " ";
		inOrder1(root->rchild);
	}
}

2.非递归

 对于任一结点P,

   1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;

   2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;

   3)直到P为NULL并且栈为空则遍历结束。

void inOrder2(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *p = root;
	while (p != NULL || !s.empty())
	{
		while (p != NULL)
		{
			s.push(p);
			p = p->lchild;
		}
		if (!s.empty())
		{
			p = s.top();
			cout << p->data << " ";
			s.pop();
			p = p->rchild;
		}
	}
}

3.后序遍历

1.递归

void postOrder1(BinTree *root)    
{
	if (root != NULL)
	{
		postOrder1(root->lchild);
		postOrder1(root->rchild);
		cout << root->data << " ";
	}
}

2.非递归

要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

void postOrder3(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *cur;                      //当前结点 
	BinTree *pre = NULL;                 //前一次访问的结点 
	s.push(root);
	while (!s.empty())
	{
		cur = s.top();
		if ((cur->lchild == NULL&&cur->rchild == NULL) ||
			(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
		{
			cout << cur->data << " ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
			s.pop();
			pre = cur;
		}
		else
		{
			if (cur->rchild != NULL)
				s.push(cur->rchild);
			if (cur->lchild != NULL)
				s.push(cur->lchild);
		}
	}
}

2.层序遍历

void printTree(BinTree *root)//层序遍历
{
	queue<BinTree *> s;
	s.push(root);
	while (!s.empty())
	{
		BinTree *p = s.front();
		cout << p->data << "";
		s.pop();
		if (p->lchild != NULL)
			s.push(p->lchild);
		if (p->rchild != NULL)
			s.push(p->rchild);
	}
}

3.求二叉树高度(深度)

int Depth(BinTree* root)
{
	if (root == NULL)
		return 0;
	int left = Depth(root->lchild);
	int right = Depth(root->rchild);
	return left > right ? left + 1 : right + 1;
}

4.求叶子节点个数

size_t GetLeafSize(BinTree* root)//求叶子节点数
{
	if (root == NULL)
		return 0;

	if (root->lchild == NULL
		&& root->rchild == NULL)
	{
		return 1;
	}

	return GetLeafSize(root->lchild)
		+ GetLeafSize(root->rchild);
}

5.求二叉树第K层节点个数

1.求二叉树第K层节点个数(递归)

int get_k_level_number(BinTree *root, int k)
{
	if (root == NULL || k <= 0)
		return 0;
	if (root != NULL && k == 1)
		return 1;

	return (get_k_level_number(root->lchild, k - 1) +
		get_k_level_number(root->rchild,k - 1));
}

2.非递归实现求二叉树第k层的节点数

6.判断一个节点是否在一棵二叉树上

bool IsInTree(BinTree* root, int x)
{
	if (root == NULL)
		return false;
	if (root->data == x)
		return true;
	return IsInTree(root->lchild, x)
		|| IsInTree(root->rchild, x);
}

7.求两个节点的最近公共祖先

1.搜索树

1 某一个节点为根节点,那么公共节点就是根节点了。

2 这俩个节点在不同子树,那么公共节点就是根节点。

3 这俩个节点在同一子树。

所以根据以上分析,可得我们可以通过不断的递归直到找到一个子树,在该子树中俩节点分别位于它的左子树和右子树,那么该子树节点就是公共节点。

BinTree* findCom_ancestor(BinTree *root, BinTree *x, BinTree *x2)
{
	if (root == NULL) return NULL;
	if (x == root || x2 == root) return root;
	if (x->data > root->data&&x2->data < root->data)
	{
		return root;
	}
	if (x->data < root->data&&x2->data > root->data)
	{
		return root;
	}
	if (x->data < root->data)
	{
		return findCom_ancestor(root->lchild, x, x2);
	}
	else
	{
		return findCom_ancestor(root->rchild, x, x2);
	}
}

2.三叉链

1.给定的两个节点都含有父节点,因此,可将这两个节点看做是两个链表的头结点,将求两个节点的最近公共祖先节点转化为求两链表的交点,这两个链表的尾节点都是根节点。(O(n))

int Hight(BinTree* root, BinTree* node)

{
	int len = 0;
	while (node != NULL)
	{
		len++;
		node = node->parent;
	}
	return len;
}
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)

{
	if (root == NULL || node1 == NULL || node2 == NULL)

		return NULL;

	int len1 = Hight(root, node1);

	int len2 = Hight(root, node2);

	for (; len1 > len2; len1--)

		node1 = node1->parent;

	for (; len2 > len1; len2--)

		node2 = node2->parent;

	while (node1 && node2 && node1 != node2)
	{
		node1 = node1->parent;

		node2 = node2->parent;
	}

	if (node1 == node2)

		return node1;

	else

		return NULL;

}

2.首先给出node1的父节点node1->_parent,然后将node1的所有父节点依次和node2->parent作比较,如果发现两个节点相等,则该节点就是最近公共祖先,直接将其返回。如果没找到相等节点,则将node2的所有父节点依次和node1->_parent->_parent作比较......直到node1->_parent==NULL。O(n^2)

BinTree * GetLastCommonAncestor(BinTree * root, BinTree * node1, BinTree * node2)

{

	BinTree * temp;

	while (node1 != NULL)

	{

		node1 = node1->parent;

		temp = node2;

		while (temp != NULL)

		{

			if (node1 == temp->parent)

				return node1;

			temp = temp->parent;

		}

	}

}

3.一般二叉树

从根节点开始遍历,如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。

如果都不匹配,则分别递归左、右子树,

如果有一个 节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先. 

如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。

该函数当一个节点是另一个节点的祖先时,返回的是离根节点最近的那个节点,要想返回最近公共祖先节点需进行判断两节点是否有祖孙关系,

若两节点为F,D,则判断出B的左子树中的D节点后,继续判断判断节点D的左右子树中是否含有节点F,若有,则返回B,若没有则继续判断右子树中的另一个节点。

BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)

{

	if (root == NULL || node1 == NULL || node2 == NULL)

		return NULL;



	if (node1 == root || node2 == root)

		return root;



	BinTree* cur = NULL;



	BinTree* left_lca = GetLastCommonAncestor(root->lchild, node1, node2);

	if (NULL != left_lca)

	{

		cur = GetLastCommonAncestor(left_lca->lchild, node1, node2);

		if (cur == NULL)

			cur = GetLastCommonAncestor(left_lca->rchild, node1, node2);

		if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))

			return root;

	}

	BinTree* right_lca = GetLastCommonAncestor(root->rchild, node1, node2);

	if (NULL != right_lca)

	{

		cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);

		if (cur == NULL)

			cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);

		if ((cur == node1) && (right_lca == node2) || (cur == node2) && (right_lca == node1))

			return root;

	}

	if (left_lca && right_lca)

		return root;

	if (left_lca == NULL)

		return right_lca;

	else

		return left_lca;

}

8.判断一棵二叉树是否是平衡二叉树

//1
bool IsBalanceTree(BinTree* root)//(时间复杂度是N^2)
{
	if (root == NULL)
		return true;
	int left = Depth(root->lchild);
	int right = Depth(root->rchild);
	return abs(left - right)<2	   
		&& IsBalanceTree(root->lchild)
		&& IsBalanceTree(root->rchild);
}
//2
bool IsBalance(BinTree* root, int& depth)//(时间复杂度是N)
{
	if (root == NULL)
	{
		depth = 0;
		return true;
	}
	int leftDepth = 0;
	if (IsBalance(root->lchild, leftDepth) == false)
		return false;
	int rightDepth = 0;
	if (IsBalance(root->rchild, rightDepth) == false)
		return false;
	depth = leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
	return abs(leftDepth - rightDepth) < 2;
}

9.求二叉树中最远两个节点的距离

int _FindMaxLen(BinTree* root, int& maxLen)
{
	if (root == NULL)
		return 0;

	int left = _FindMaxLen(root->lchild, maxLen);
	int right = _FindMaxLen(root->rchild, maxLen);

	if (left + right > maxLen)
	{      
		maxLen = left + right;
	}

	return left > right ? left + 1 : right + 1;
}

10. 由前序遍历和中序遍历重建二叉树

BinTree* _RebuildBinaryTree(int* startPre, int* endPre, int* startIn, int* endIn)
{
	//根据前序遍历保存根节点
	BinTree *Root;
	Root = new BinTree();
	Root->data = startPre[0];
	Root->lchild = NULL;
	Root->rchild = NULL;
	//判断是否找完了此次中序遍历,若是找完了,则返回Root;
	if (startIn == endIn&&*startIn == *endIn)
		return Root;
	//在中序遍历中找根节点所在的位置
	int* RootIn = startIn;
	while (*startPre != *RootIn)
		RootIn++;
	//根据根节点在中遍历中的位置,递归还原左子树
	int leftlen = RootIn - startIn;
	if (leftlen > 0)
	{
		Root->lchild = _RebuildBinaryTree(startPre + 1, startPre + leftlen, startIn, RootIn-1);
	}
	//左子树长度加上中序的起始位置后若仍小于整个中序长度,则说明该节点右子树存在,递归还原右子树
	if (leftlen + startIn < endIn)
	{
		Root->rchild = _RebuildBinaryTree(startPre + leftlen + 1, endPre, startIn + leftlen + 1, endIn);
	}
	return Root;
}

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

//->关键:找第一个度不为2的结点->后序结点:如果有孩子则不是完全二叉树,否则是
bool IsCompleteBinaryTree(BinTree* root)
{
	if (NULL == root)  //
		return false;
	bool isOnlyLeft = false;  //标记仅有左节点的结点
	queue<BinTree*> q;
	q.push(root);
	while (!q.empty())
	{
		BinTree* pCur = q.front();
		q.pop();
		if (isOnlyLeft)
		{
			if (pCur->lchild || pCur->rchild)
				return false;
		}
		else
		{
			if (NULL == pCur->lchild && NULL != pCur->rchild
				)  //存在右孩子没有左孩子,一定不为完全二叉树
				return false;
			else if (NULL != pCur->lchild && NULL == pCur->rchild)  //存在左孩子没有右孩子可能不是,记录标记结点看后续结点
			{
				q.push(pCur->lchild);
				isOnlyLeft = true;  //只有左孩子是非满结点
			}
			else if (NULL != pCur->lchild  && NULL != pCur->rchild)  // 左右孩子都存在,入队列继续循环判断 
			{
				q.push(pCur->lchild);
				q.push(pCur->rchild);
			}
			else
				isOnlyLeft = true; //左右孩子都存在,为非满结点,看后续结点
		}
	}
	return true;
}

12.求二叉树的镜像(即左右子树交换)

BinTree * Mirror(BinTree * Root)
{
	if (Root == NULL) // 返回NULL  
		return NULL;
	BinTree * pLeft = Mirror(Root->lchild); // 求左子树镜像  
	BinTree * pRight = Mirror(Root->rchild); // 求右子树镜像  
	// 交换左子树和右子树  
	Root->lchild = pRight;
	Root->rchild = pLeft;
	return Root;
}

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

BinTree* Convert(BinTree* root)
{
	if (root == NULL){//假如根节点为空,返回空
		return NULL;
	}
	if (root->lchild == NULL&&root->rchild == NULL){//假如只有一个根节点,则返回根节点
		return root;
	}
	//1、将左子树构造成双链表,并返回该链表头结点left
	BinTree* left = Convert(root->lchild);

	//2、定位到左子树链表的最后一个节点(左子树最右边的节点)
	BinTree* p = left;//创建一个临时节点P,用来遍历找到左链表的最后一个节点(左子树最右边的节点),p初始化指向做左子树的根节点,
	while (p != NULL&&p->rchild != NULL)
	{
		p = p->lchild;//最终p为左子树最右边的节点
	}

	//3、如果左子树链表不为空,将当前root追加到左子树链表后
	if (left != NULL){//左子树链表不为空
		p->rchild = root;//左子树链表的最后一个节点p(左子树最右边节点)的右指针指向当前root节点
		root->lchild = p;//当前root节点的左指针指向左子树链表的最后一个节点p(左子树最右边节点)
	}

	//4、将右子树构造成双链表,并返回该链表的头结点right
	BinTree* right = Convert(root->rchild);

	//5、如果右子树链表不为空,将右子树链表追加到当前root后
	if (right != NULL)
	{//右子树链表不为空
		right->lchild = root;//右子树链表的头结点right的左指针指向当前root
		root->rchild = right;//当前root的右指针指向右子树链表的头结点right
	}
	return left != NULL ? left : root;//根据左子树链表是否为空返回整个双向链表的头指针。  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值