二叉树常见的面试题

1.求一颗二叉树是否平衡,

typedef struct node
{
	struct node*left;
	struct node*right;
	int val;
}node;
int maxDepth(node*root)
{
	if(root==NULL)
		return 0;
	return 1+max(maxDepth(root->left),maxDepth(root->right));
}

int minDepth(node*root)
{
	if(root==NULL)
	{
		return 0;
	}
	return 1+min(minDepth(root->left),minDepth(root->right));
}

bool isBanlancde(node*root)
{
	if(root==NULL)
		return true;
	return (maxDepth(root) -minDepth(root)<=1);
}


2.求二叉树的深度
 

  1 
2   3


第一次,遍历1,遍历2,遍历3
第二次  遍历2:2的左节点为NULL,返回0,遍历2的右节点为空 ,返回0,所以遍历2的函数中,2的左右都返回0,return 1;同理在遍历1的时候,2返回了返回1,接下来执行遍历1的右节点,遍历3,遍历3也返回1,所以,遍历1的函数中,遍历2和遍历3都返回了1,所以最后是2.也就是遍历root的结果
如果3的下面还有,比如3返回1,所以2返回0,3返回1,那就不一样了,最后要多加一个1。因为又多了一层返回。
int treeDepth(node*root	)
{
	if(root==NULL)
	{
		return 0;
	}
	int left=treeDepth(root->left);
	int rigth=treeDepth(root->rigth);
	return left>right?left+1:right+1;
}


   		1
 	2      	3
  4  5     6      根据这个结构,进行体会。
  总之就是要把每一层的节点都加入到while(循环里)的一次循环中,也就是cur记录本层循环树,那么我这层的几个节点的孩子
  就属于同一层,即使是3个的话,也会while(cur),此时的cur已经被加成3了。所以循环完毕,才会看队列中是否有下一层的东西。


    	1
 	2      	3
  4        5 
 

#include <queue>
int treeDepth(node*root)
{
	if(root==NULL)
		return 0;
	queue<node*> _queue;
	int nodeNum=0;记录队列节点数
	_queue.push(root);
	int cur=1;
	int level = 0;
	while(!_queue.empty())
	{


		while(cur)//把这一层的都给循环完,再循环下一层,这应该是广度优先遍历。
		{
		node*tmp = _queue.front();
		_queue.pop();
		cur--;
		if(tmp->left)
		{
			_queue.push(tmp->left);
			nodeNum++;		
		}
		if(tmp->right)
		{
			_queue.push(tmp->right);
			nodeNum++;		

		}
		}
		cur=nodeNum;//记录每个节点下的孩子,然后循环全部孩子节点
		nodeNum=0;
		level++;

	}
	return level;
}


3.求二叉树叶子节点到根节点的最小高度

       1
      2  3
     4



	假如求最小高度,也就是1这个树
	的最小高度,他一定是1+2和3的最小高度中的更小的一个,然后以此循环,代码就是上面的简单的代码
。这样很好理解。	

		1
	2        3
 4     5    
 		  6


4节点的深度为1
5节点的深度为2

所以2节点的深度为1+1(最小的)

3节点的深度为1

所以min(1,1+1) +1 ,就是2,也就是3这个节点。


if循环判断的逻辑应该这么思考:
1)6的左右子树都为空,算上本身6的节点,所以返回1
2)而5,左子树为空,高度为0,右子树6的高度为1,所以只有右子树,高度为(右子树的高度+1)=2,所以求得的最小高度是2(也是5的高度,谈不上最小高度)
	同样右子树为空,左不为空,

3)而2这个节点,它的左右子树都有,也就是非叶子节点,所以就得比较了,求出最小的那个,(左右子树高度最小那个+2本身的高度1)

以上就是4个循环条件。

对于空节点,应该返回的是0,因为相当于本身这个节点的左右子树的高度都为0,而最后循环确实left==0&&right==0,考虑的就是本身这个节点的高度1,所以返回1

int depth(node*root)
{
	if(root==NULL)
	{
		return 0;
	}

	int left=depth(root->left);
	int right=depth(root->right);
	if(left==0&&right==0)//说明是叶子节点,他的左右节点为空,算上叶子本身的高度,为1,所以返回1
	{
		return 1;
	}
	else if(left==0)//只有右子树,此时这个节点的高度就是这个节点root的root->right的right+1;
	{
		return 1+right;
	}
	else if(right==0)//只有左子树,那么这个节点的高度就是左节点的高度+1
	{
		return 1+left;
	}
	else //如果这个树的左右节点高度不相等,那么取最小值
	{
		return min(left,right)+1;
	}
}
typedef struct tree
{
	struct tree*left, *right;
	int val;
}tree;

//判断一颗二叉树是否为平衡二叉树

bool isBanlancdeBinary(node*root)
{
	if(root==NULL)
		return true;
	int leftDepth=treeDepth(root->left);
	int rightDepth=treeDepth(root->right);
	int diff=leftDepth-rightDepth;
	if(diff>1||diff<-1)
	{
		return false;//只要有一个节点的左右高度差,不满足,直接返回
	}
	return isBanlancdeBinary(root->left)&&isBanlancdeBinary(root->right);
}//当最后一个节点判断完毕的时候,如果左子树平衡,
//方法2:
bool  isBalanceBinaryTree(tree*root, int*depth)
{
	if (root == NULL)
	{
		*depth = 0;
		return true;
	}
	int left;
	int right;
	if (isBalanceBinaryTree(root->left, &left) && isBalanceBinaryTree(root->right, &right))
	{
		int diff = left - right;
		if (diff <= 1 && diff >= -1)
		{
			//return left > right ? left + 1 : right + 1;
			*depth = left > right ? left + 1 : right + 1;
			return true;
		}
	}
	return false;
}

//方法3
/*

			1
		2		3
	4
5

1 2(-1) 3         1的左子树不平衡,diff=2;所以直接返回-1,调用1,返回-1,所以不是AVL
2 4(2) null(0) return -1(只要有一个return -1,就证明是不平衡的了.一直向函数调用最开始进行传递
4 5(1) null(0) return 2
5 null(0) null(0) return 1;//return返回的都是当前节点的深度
*/

int checkheight(tree*root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = checkheight(root->left);
	if (left == -1)
		return -1;
	int right = checkheight(root->right);
	if (right == -1)
		return -1;
	int diff = left - right;
	if (diff <= 1 && diff >= -1)
	{
		return max(left, right) + 1;
	}
	else
	{
		return -1;
	}
}
bool isAVL(tree*root)
{
	if (checkheight(root) == -1)
	{
		return false;
	}
	else
	{
		return true;
	}
}

要学会思考和推演,用简单的例子来推演出全局的.

4 将一颗二叉树按照先序遍历顺序展开成一个单链表。(leetcode)

/*
	 1
 	2 3
   4  5
   1 2 4 5 3
*/
#include <stack>
class solu
{
public:

	void flattern(tree*root)//root还是指向根
	{

		if (root == NULL)
			return;
		vector<tree*> vec;
		preOrder(root, vec);
		tree*tmp = root;//tmp也就是vec的第一个节点vec[0],也就是根节点
		for (int i = 1; i < vec.size(); i++)
		{
			tmp->left = NULL;
			tmp->right = vec[i];//第一次指向第二个节点
			tmp = tmp->right;

		}
		tmp->left = tmp->right = NULL;//tmp指向了最后一个节点,让最后一个节点的left和right为空
	}

	void preOrder(tree*root, vector<tree*> &vec)
	{
		
		stack<tree*> s;
		while (root!=NULL||s.size()>0)
		{
			while (root!=NULL)
			{
				s.push(root);
				vec.push_back(root);
				root = root->left;
			}

			if (s.size() > 0)
			{
				tree*tmp = s.top();
				s.pop();
				root = tmp->right;

			}


		}

	}
	void createTree(tree*root)
	{
		int i;
		cin >> i;
		if (i != 0)
		{
			root = new tree(i);
			if (root == NULL)
				return;
			createTree(root->left);
			createTree(root->right);

		}
	}
};
1  1left 1rigth
1left 2   2left 2rigth
2left 3 3left 3right
3left 0 return 
3right 4 return
0 0
5 
0 0
6
0 0
*/
//对的输入方式
//1 2 3 0 4 0 0 5 0 0 6 0 0 ,输入注意一下,有时候一个节点得需要输入2个0


将root的右子树中的链接到左子树的最后一个结点,然后将整个左子树作为根的右子树,此时,根只有一颗右子树,root->left=NULL。然后将root移到右子树第一个节点,此时是以右子树为根节点的子树,重复上面的过程。直到为NULLvoid flattern1(tree* root)
{
	while (root)
	{
		if (root->left)//得加判断
		{

			tree*pre = root->left;
			while (pre->right)
			{
				pre = pre->right;
			}
			pre->right = root->right;
			root->right = root->left;
			root->left = NULL;
		}

		root = root->right;
	}
}
void main()
{
	//printf("13344\n");
	tree*root;
	solu s;
	vector<tree*> vec;
	s.createTree(root);
#if 0
	s.preOrder(root, vec);
	s.flattern(root);
	while (root)
	{
		printf("%d\n",root->val);
		root = root->right;

	}
#endif

#if 1
	flattern1(root);
	while (root)
	{
		printf("%d\n", root->val);
		root = root->right;

	}
#endif
}

//二叉树的后续遍历

struct node
{
	struct node*left;
	struct node*right;
	int val;
	node(int _val) :val(_val), left(NULL), right(NULL)
	{

	}
};
#include<stack>
 void postOrder(node*root)
 {
	 if (root == NULL)
		 return;
	 std::stack<node*>tmp;
	 std::stack<node*>output;

	 while (root!=NULL||!tmp.empty())
	 {
		 if (root != NULL)
		 {
			 tmp.push(root);
			 output.push(root);
			 root = root->right;
		 }
		 else
		 {
			 node*t = tmp.top();
			 tmp.pop();
			 root = t->left;
		 }

	 }
	
	 while (output.size()>0)
	 {
		 node*result = output.top();
		 printf("%d\n", result->val);
		 output.pop();
	 }


 }

//判断是否为二叉树的子结构

//写对了,分析问题的方式,就是从头开始想,一个二叉树,和一个子二叉树,然后第一次开始判断,根是否为空,或者子树是否为空,如果为空,说明2个中的任意
//一个还没有在issub函数return true,而已经为空了,所以就return false.
//判断是否为二叉树的子结构
//当确定了root节点中的一个点跟sroot是一样的,就开始遍历root下每一个点
bool isSub(tree*root, tree*sroot)
{
	if (sroot == NULL)
	{
		return true;
	}
	if (root == NULL)
	{
		return false;
	}
	if (root->val != root->val) //
	{
		return false;
	}
	return isSub(root->left, sroot) && isSub(root->right, sroot);
}
bool isSubTree(tree*root, tree*sroot)
{
	if (root == NULL || sroot == NULL)
		return false;
	bool result = false;
	if (root->val == sroot->val)//第一个判断根节点是否和子树根节点一样, 如果不一样的话,就继续判断root的左子树,看是否一样.而如果一样的话,等issub函数执行完,以此时的root为根的结果如果为false,就是不是子树,继续下一次,就是root的left,再找下一个
		//和子树的val相等的下一个节点,如果不相等,继续,找子树的下一个节点,加入左子树都找完了,那么就开始右子树的查找,同样的循环,如果右子树中有一个根节点一样和sroot,那么就成功进入issbu
		//函数,判断以此时的节点为root,来递归判断,二叉树sroot和此时root是否相等,issub就是,看root的左子树和srrot的左子树是否相等,不相等返回false,issub执行结束,相等的话,继续root->left->left和sroot->left->left判断.假如判断到了root和sroot都为空
		//issub函数中先判断sroot是否为空,如果为空,就证明了sroot的左子树都判读完了,最开始根节点相等root和sroot那个相等的左子树判断完了,继续&&issub右子树),同样的道理,如果中间有不相等的,因为经过了判断root或者sroot为空,所以如果判断不相等,一定是不相等,因为还么有遍历完root或者sroot的节点
		//所以issub返回,并且返回到issubtree中,result为false,则issubtrereturn false,继续issubtree刚才的函数调用栈
	{
		result =isSub(root, sroot);
	}
	if (!result)
	{
		result = isSubTree(root->left, sroot);
	}
	if (!result)
	{
		result = isSubTree(root->right, sroot);
	}

	return result;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值