二叉树基本操作及面试题(你想要的都有)

在几乎所有的二叉树操作中,尤其是在递归操作里,我们会发现其实质上是把每一个节点都当作是一棵树来处理,按照应有的规则去操作这棵树,保证在操作这一棵树不出现问题,那么所有的操作也将会跟着进行。特别要注意以下两个问题:
(1)递归的出口。
(2)递归结束需要改变值就传二级指针。
定义如下:

typedef char BDataType;
typedef struct BTNode
{
	BDataType data;
	struct BTNode* Lchild;
	struct BTNode* Rchild;
}BTNode;

创建节点:

BTNode *BuyBTNode(BDataType data)
{
	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	if(NULL == newNode)
	{
		assert(0);
		return NULL;
	}
	newNode->Lchild = NULL;
	newNode->Rchile = NULL;
	newNode->data = data;
	return newNode;
} 

1. 创建二叉树。(前序创建)
(1) 若序列索引小于序列长度并且当前索引元素为有效元素。
(2) 创建根节点。
(3) 创建根节点的左子树。
(4) 创建根节点的右子树。

//因为每次递归结束需要改变root所指内容,所以传二级指针。index同理。
void CreateBinTree(BTNode **root,char *str,int size,int *index)
{
	assert(root);
	assert(index);
	if(*index < size && str[*index] != '#')
	{
		*root = BuyBTNode(str[*index]);
		++(*index);
		CreateBinTree(&(*root)->Lchild,str,size,index);
		++(*index);
		CreatrBinTree(&(*root)->Rchild,str,size,index);
	}
}

2. 前序遍历递归实现。
(1)遍历根节点。
(2)遍历根节点的左子树。
(3)遍历根节点的右子树。

void PreOrder(BTNode *root)
{
	if(root)
	{
		printf("%c ",root->data);
		PreOrder(root->Lchild);
		PreOrder(root->Rchild);
	}
}

3. 中序遍历递归实现。
(1)遍历根节点的左子树。
(2)遍历根节点。
(3)遍历根节点的右子树。

void InOrder(BTNode *root)
{
	if(root)
	{
		InOrder(root->Lchild);
		printf("%c ",root->data);
		InOrder(root->Rchild);
	}
}

4. 后序遍历递归实现。
(1)遍历根节点的左子树。
(2)遍历根节点的右子树。
(3)遍历根节点。

void PostOrder(BTNode *root)
{
	if(root)
	{	
		PostOrder(root->Lchild);
		PostOrder(root->Rchild);
		printf("%c ",root->data);
	}
}

5. 复制二叉树。
操作类似二叉树前序遍历。
(1)拷贝根节点。
(2)拷贝根节点的左子树。
(3)拷贝根节点的右子树。

BTNode *CopyBinTree(BTNode *root)
{
	BTNode *newRoot = NULL;
	if(root)
	{
		newRoot = BuyBTNode(root->data);
		if(root->Lchild)
			newRoot->Lchild = CopyBinTree(root->Lchild);
		if(root->Rchild)
			newRoot->Rchild = CopyBinTree(root->Rchild); 
	}
	return newRoot;
}

6. 求二叉树中节点的个数。
(1)左右子树中节点的个数。

int GetBTNodeCount(BTNode *root)
{
	if(NULL == root)
	{
		return 0;
	}
	return GetBTNodeCount(root->Lchild)+GetBTNodeCount(root->Rchild)+1;
}

7. 求二叉树中叶子节点的个数。
叶子节点:既没有左孩子,也没有右孩子。
(1)左右子树中叶子节点的个数。

int GetLeafNodeCount(BTNode *root)
{
	if(NULL == root)
	{
		return 0;
	}
	if(NULL == root->Lchild && NULL == root->Rchild)
	{
		return 1;
	}
	return GetLeafNodeCount(root->Lchild)+GetLeafNodeCount(root->Rchild);
}

8. 求二叉树中第K层节点的个数。
(1)求左右字数中第K-1层节点的个数。

int GetKLevelNodeCount(BTNode *root,int K)
{
	if(NULL == root)
	{
		return 0;
	}
	if(K == 1)
	{
		return 1;
	}
	return GetKLevelNodeCount(root->Lchild,K-1)+GetKLevelNodeCount(root->Rchild,K-1);
}

9. 求二叉树的高度。
(1)求二叉树中左右子树较高树的高度。

int GetBinTreeHeight(BTNode *root)
{
	if(NULL == root)
	{
		return 0;
	}
	int LeftHeight = 0;
	int RightHeight = 0;
	LeftHeight = GetBinTreeHeight(root->Lchild);
	RightHeight = GetBinTreeHeight(root->Rchild);
	
	return LeftHeight>RightHeight?LeftHeight+1:RightHeight+1;	
}

10. 销毁二叉树
(1)销毁根节点的左子树。
(2)销毁根节点的右子树。
(3)销毁根节点。

void DestroyBinTree(BTNode **root)
{
	assert(*root);
	if(NULL == *root)
	{
		return;
	}
	DestroyBinTree(&(*root)->Lchild);
	DestroyBinTree(&(*root)->Rchild);
	free(*root);
	*root = NULL;
}

11. 求二叉树的左孩子节点。

BTNode *LeftNode(BTNode *root)
{
	if(NULL == root)
	{
		return NULL;
	}
	return root->Lchild;
}

12. 求二叉树的右孩子节点。

BTNode *LeftNode(BTNode *root)
{
	if(NULL == root)
	{
		return NULL;
	}
	return root->Rchild;
}

13. 求二叉树中最远的节点之间的距离。
(1)计算当前结点的深度以及以当前结点为根的子树的最大距离。

int Max_distance(BTNode *root, int *m)
{
	if (NULL == root)
	{
		return 0;
	}
	int* max = m;
	int LeftDepth = Max_distance(root->Lchild, max);
	int RightDepth = Max_distance(root->Rchild, max);

	if (*max < LeftDepth + RightDepth)
	{
		*max = LeftDepth + RightDepth;
	}
	return LeftDepth>RightDepth ? LeftDepth + 1 : RightDepth + 1;
}

(2)计算二叉树中最远节点距离。

int Maxdistance(BTNode *root)
{
	if (NULL == root)
	{
		return 0;
	}
	int max = 0;
	Max_distance(root, &max);
	return max;
}

14. 判断一个节点是否在二叉树中。
(1)判断是否为根节点。若不是,则转到(2)。
(2)判断是否为左子树中的节点。
(3)判断是否为右子树中的节点。

int IsBTNodeInBinTree(BTNode *root,BTNode *Node)
{
	if((NULL== root)||(NULL == Node))
		return 0;
		
	if(Node == root)
		return 1;
		
	if(IsBTNodeInBinTree(root->Lchild,Node))
		return 1;
	
	return IsBTNodeInBinTree(root->Rchild,Node);
}

15. 求一个节点的最近的祖先节点。
(1)从根节点开始判断。
(2)如果根节点的左孩子或者右孩子为该节点,那么返回根节点。
(3)更新根节点为其左孩子。转到(1)。
(4)更新根节点为其右孩子。转到(1)。

BTNode* GetBTNodeParent(BTNode *root,BTNode *Node)
{
	if((NULL == root)||(NULL == Node)||(root == Node)
		return 0;
	if((Node == root->Lchild) || (Node == root->Rchild))
		return root;
	
	BTNode *parent = NULL;
	if(parent = GetBTNodeParent(root->Lchild,Node))
		return parent;

	return GetBTNodeParent(root->Rchild,Node);
}

16. 求二叉树的镜像。
(1)交换根节点的左右孩子。
(2)交换左子树的左右孩子。
(3)交换右子树的左右孩子。

void MirrorBinTree(BTNode *root)
{
	if(root)
	{
		Swap(&root->Lchild,&root->Rchild);
		MirrorBinTree(BTNode *root->Lchild);
		MirrorBinTree(BTNode *root->Rchild);
	}
}

17. 层序遍历。
将根节点放到队列中。
(1) 取队头元素。
(2) 访问该元素。
(3) 如果该节点的左子树存在,入队列。
(4) 如果该节点的右子树存在,入队列。
(5) 将对头元素出队列。
(6) 判断队列是否为空,若空,则结束,否则转到(1)。

void LevelOrder(BTNode *root)
{
	if(NULL == root)
		return;
	
	Queue q;
	QueueInit(&q);
	QueuePush(&q,root);
	while(!QueueEmpty(&q))
	{
		BTNode *cur = QueueFront(&q);
		printf("%c ",cur->data);
		if(cur->Lchild)
			QueuePush(&q,cur->Lchild);

		if(cur->Rchild)
			QueuePush(&q,cur->Rchild);

		QueuePop(&q);
	}
}

18. 判断是否为完全二叉树。
将根节点放到队列中。
(1)取队头元素。
(2)如果根节点既有左孩子,也有右孩子,则均入队列。
(3)如果根节点只有左孩子,没有右孩子,则为完全二叉树。
(4)如果根节点只有右孩子,没有左孩子,则不是完全二叉树。
(5)如果根节点没有左孩子,没有右孩子,则为完全二叉树。
(6)出队列,并判断是否为空队列。为空跳出,不为空则转到(1)。

int IsCompleteBinTree(BTNode *root)
{
	if(NULL == root)
		return 0;
	
	int IsFlag = 0;
	Queue q;
	QueueInit(&q);
	QueuePush(&q,root);
	while(!QueueEmpty(&q))
	{
		BTNode *cur = QueueFront(&q);
		if(IsFlag)
		{
			if(cur-Lchild || cur->Rchild)
				return 0;
		}
		else
		{
			if(cur->Lchild && cur->Rchild)
			{
				QueuePush(&q,cur->Lchild);
				QueuePush(&q,cur->Rchild);
			}
			else if(cur->Lchild)
			{
				QueuePush(&q,cur->Lchild);
				IsFlag = 1;
			}
			else if(cur->Rchild)
			{
				return 0;
			}
			else
			{
				IsFlag = 1;
			}
		}
		QueuePop(&q);
	}
	return 1;
}

19. 前序遍历非递归实现。
因为前序遍历的顺序是根–>左–>右,所以通过栈实现的时候,压入根节点之后需要压入的是有孩子节点,而不是左孩子节点。
(1) 遍历以cur为根的二叉树的根节点。同时出栈栈顶元素。若栈为空,则跳出,否则进行下一步。
(2) 如果有右孩子,将其压入栈中。
(3) 如果有左孩子,将其压入栈中。

void PreOrderNor1(BTNode *root)
{
	if(NULL == root)
		return;

	Stack s;
	StackInit(&s);
	StackPush(&s,root);
	while(!StackEmpty(&s))
	{
		BTNode *cur = StackTop(&s);
		printf("%c ",cur->data);
		StackPop(&s);
		if(cur->Rchild)
			StackPush(&s,cur->Rchild);

		if(cur->Lchild)
			StackPush(&s,cur->Lchild);
	}
}

void PreOrderNor2(BTNode *root)
{
	if(NULL == root)
		return;

	Stack s;
	StackInit(&s);
	StackPush(&s,root);
	while(!StackEmpty(&s))
	{
		BTNode *cur = StackTop(&s);
		StackPop(&s);
		while(cur)
		{
			printf("%c ",cur->data);
			if(cur->Rchild)
				StackPush(&s,cur->Rchild);
			
			cur = cur->Lchild;
		}
	}
}

20. 中序遍历非递归实现。
(1) 找以cur为根的二叉树最左侧的节点,并保存所经路径中的节点。(保存通过压栈来实现)
(2) 遍历以cur为根的二叉树的根节点。
(3) 遍历以cur为根的二叉树的右孩子。如果右子树存在,转到(1),否则回退。(回退操作通过取栈顶实现)

void InOrderNor(BTNode *root)
{
	if(NULL == root)
		return;
	
	Stack s;
	StackInit(&s);
	BTNode *cur = root;
	while(cur || !StackEmpty(&s))
	{
		while(cur)
		{
			StackPush(&s,cur);
			cur = cur->Lchild;
		}
		cur = StackTop(&s);
		printf("%c ",cur->data);
		StackPop(&s);
		cur = cur->Rchild;
	}
}

21. 后序遍历非递归实现。
(1) 找以cur为根的二叉树最左侧的节点,并保存所经路径中的节点。(保存通过压栈来实现)
(2) 判断当前栈顶元素是否有右孩子,如果没有,遍历当前节点,否则更新cur为其右孩子节点。

void PostOrderNor(BTNode *root)
{
	if(NULL == root)
		return;
	
	Stack s;
	StackInit(&s);
	BTNode* visitNode = NULL;
	BTNode* cur = root;
	while(cur || !StackEmpty(&s))
	{
		while(cur)
		{
			StackPush(&s,cur);
			cur = cur->Lchild;
		}
		cur = StackTop(&s);
		if(NULL == cur->Rchild || visitNode == cur->Rchild)
		{
			printf("%c ",cur->data);
			StackPop(&s);	
			visitNode = cur;
			cur = NULL;
		}
		else
		{
			cur = cur->Rchild;
		}
	}
}

可能有些知识点掌握不到位,还请各位读者在评论区多给意见!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值