二叉树算法题总结

1.二叉树的节点数计算

class CountNodes {
public:
    int count(TreeNode* root) {
        if (!root)
		return 0;
	int size = 1 +count(root->left);
	size += count(root->right);
	return size;
    }
};

2.二叉树的高度计算

int BinaryTree::height()
{
	if (!Root)
		return 0;
	return ret_height_Core(Root);
}
int BinaryTree::ret_height_Core(TreeNode * x)
{
	if (!x)
		return 0;
	int left_depth = ret_height_Core(x->left_child);
	int right_depth = ret_height_Core(x->right_child);
	return max(left_depth,right_depth)+1;
}

3.请用递归方式实现二叉树的先序,中序和后序的遍历打印。

给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式)。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class TreeToSequence {
public:
void pre_print(vector<int>& vec,TreeNode* root)
{//二叉树的递归前序遍历
	if (!root)
		return;
	vec.push_back(root->val);
	pre_print(vec, root->left);
	pre_print(vec, root->right);
}
void mid_print(vector<int>& vec, TreeNode* root)
{//二叉树的递归中序遍历
	if (!root)
		return;
	mid_print(vec, root->left);
	vec.push_back(root->val);
	mid_print(vec, root->right);
}
void aft_print(vector<int>& vec, TreeNode* root)
{//二叉树的递归后序遍历
	if (!root)
		return;
	aft_print(vec, root->left);
	aft_print(vec, root->right);
	vec.push_back(root->val);
}
vector<vector<int> > convert(TreeNode* root) {
     vector<vector<int> > vec;
	vector<int> A,B,C;
	pre_print(A, root);
	mid_print(B, root);
	aft_print(C, root);
        vec.push_back(A);
         vec.push_back(B);
         vec.push_back(C);
	return vec;
    }
};

4.请用非递归方式实现二叉树的先序,中序和后序的遍历打印。

给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式)。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class TreeToSequence {
public:
void pre_by_stack(vector<int>& vec, TreeNode* root)
{
	stack<TreeNode*> sta;
	TreeNode* x = root;
	while (true)
	{
		while (x)
		{
			vec.push_back(x->val);
			if(x->right)
			sta.push(x->right);
			x = x->left;
		}
		if (sta.empty())
			break;
		x = sta.top();
		sta.pop();
	}
}
void mid_by_stack(vector<int>& vec, TreeNode* root)
{
	stack<TreeNode*> sta;
	TreeNode* x = root;
	while (true)
	{
		while (x)
		{
			sta.push(x);
			x = x->left;
		}
		if (sta.empty())
			break;
		x = sta.top();
		sta.pop();
		vec.push_back(x->val);
		x = x->right;
	}
}
void aft_by_stack(vector<int>& vec, TreeNode* root)
{
	stack<TreeNode*> sta,stb;
	TreeNode* x = root;
	if (!root)
		return;
	sta.push(root);
	while (!sta.empty())
	{
		x = sta.top();
		sta.pop();
		stb.push(x);
		if (x->left)
			sta.push(x->left);
		if (x->right)
			sta.push(x->right);	
	}
	while (!stb.empty())
	{
		TreeNode* t = stb.top();
		stb.pop();
		vec.push_back(t->val);
	}
}
vector<vector<int> > convert(TreeNode* root) {
	// write code here
	vector<vector<int> > vec;
	vector<int> A, B, C;
	pre_by_stack(A, root);
	mid_by_stack(B, root);
	aft_by_stack(C, root);
	vec.push_back(A);
	vec.push_back(B);
	vec.push_back(C);
	return vec;
}
};

5.有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。

给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存,所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。保证结点数小于等于500。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class TreePrinter {
public:
   vector<vector<int> > printTree(TreeNode* root) {
	// write code here
	queue<TreeNode*> q;
	vector<int> vt;
	vector<vector<int>> vec;
	TreeNode* last = root;
	TreeNode* nlast = nullptr;
	q.push(root);
	while (!q.empty())
	{
		TreeNode* temp = q.front();
		cout << temp->val << " ";
		vt.push_back(temp->val);
		q.pop();
		if (temp->left)
		{
			q.push(temp->left);
			nlast = temp->left;
		}
		if (temp->right)
		{
			q.push(temp->right);
			nlast = temp->right;
		}
		if (last->right == nlast || last->left == nlast||temp==last)
		{
			   last = nlast;
			cout << endl;
			vec.push_back(vt);
			vt.clear();
		}
	}
	return vec;
}
};

答案:新设定2个指针最后,nlast.last指向当前打印这一层的最后一个节点.nlast指向下一层的最后一个节点如果持续的左孩子或者右孩子是n上次的话,说明这一层已经打印完了,最后现在等于下一层的最后一个节点(也就是此时的ñ上次)。然后将原来那一层的元素放入待输出的序列中。同时还有一种情况需要注意,我们这一层的最后一个结点如果没有左右孩子的话,那么只判断nlast肯定不会是最后的孩子结点,所以还需要增加一个条件当前打印节点是否已经打印到了这层的末尾即temp == last 。

6.有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。

给定二叉树的根结点root,请返回一个bool值,代表这棵树是否为平衡二叉树。

int BinaryTree::ret_height_Core(TreeNode * x)
{
	if (!x)
		return 0;
	int left_depth = ret_height_Core(x->left_child);
	int right_depth = ret_height_Core(x->right_child);
	return max(left_depth,right_depth)+1;
}

bool BinaryTree::check(TreeNode * x)
{
	if (!x)
		return true;	
	int left_depth = ret_height_Core(x->left_child);
	int right_depth = ret_height_Core(x->right_child);
    unsigned int mud= abs(left_depth - right_depth);
	if (mud > 1)
		return false;
	return check(x->left_child)&&check(x->right_child);  
}

7.有一棵二叉树,请设计一个算法判断它是否是完全二叉树。

给定二叉树的根结点root,请返回一个bool值代表它是否为完全二叉树。树的结点个数小于等于500。

答案:。根据完全二叉树的性质,使用按层次遍历如果遇到只有右子树没有左子树的节点就返回假如果遇到有左子树也有右子树的正常继续遍历如果遇到左右子树都为空或者左子树不为空右子树为空则将叶置为真。然后下次过来遍历到节点就必须都得是叶子节点了,如果不满足这个条件也返回假。

class CheckCompletion {
public:
     bool chk(TreeNode* root) {
        // write code here
        if(root == NULL){
            return true;
        }
        queue<TreeNode*> Queue;
        TreeNode* node;
        bool leaf = false;
        Queue.push(root);
        while(!Queue.empty()){
            node = Queue.front();
            Queue.pop();
             if(node->left&&node->right)
             {
                 Queue.push(node->left);
                 Queue.push(node->right);
             }else if(leaf||node->left==nullptr&&node->right!=nullptr)
             {
                 if(node->left==nullptr&&node->right!=nullptr)
                     return false;
                 if(leaf&&(node->left!=nullptr||node->right!=nullptr))//如果不是叶子节点的话就返回false
                     return false; 
             }else
             {
                 leaf=true;
                 if(node->left)
                     Queue.push(node->left);
             }
                     
            }
         return true;
        }
        
};

8.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1 ,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

答:先保存前序序列的第一个节点作为根节点,然后在中序序列找,找到后会将其中序序列划分为两段,在中序序列中指定节点前的都属于左子树,后面都属于右子树。所以可以继续递归操作。

class Solution {
public:
    TreeNode* construct_tree(int* pre_order,int* in_order,int length)
    {
        if(!pre_order||!in_order||length<=0)
            return nullptr;
        int val=pre_order[0];
        TreeNode* Root=new TreeNode(val);
        int left_length=0;
        int right_length=0;
        for(int i=0;i<length;i++)
        {
            if(in_order[i]==val)
            {
                left_length=i;
                right_length=length-(left_length+1);
                break;
            }
        }
        int* pre_l=pre_order+1;
        int* in_l=in_order;
        int* pre_r=pre_order+left_length+1;
        int* in_r=in_order+left_length+1;
        Root->left=construct_tree(pre_l,in_l,left_length);
        Root->right=construct_tree(pre_r,in_r,right_length);
        return Root;
    }
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
          if(pre.empty()||vin.empty()||pre.size()!=vin.size())
              return nullptr;
        auto sz=pre.size();
        int* pre_order=new int[sz];
        int* in_order=new int[sz];
        int length=sz;
        for(vector<int>::size_type i=0;i<sz;i++)
        {
            pre_order[i]=pre[i];
            in_order[i]=vin[i];
        }
        return construct_tree(pre_order,in_order,length);
    }
};

9.输入两棵二叉树A,B,判断乙是不是一个的子结构(PS:我们约定空树不是任意一个树的子结构)。

答:首先比较主树与匹配树的根节点,只有在根节点匹配成功后,才继续判断是否是其子树。

bool is_subTree(TreeNode* pRoot1,TreeNode* pRoot2)
{
    if(!pRoot2)
      return true;
    if(!pRoot1)
     return false;
    if(pRoot1->val!=pRoot2->val)
       return false;
    return is_subTree(pRoot1->left,pRoot2->left)&&
          is_subTree(pRoot2->right,pRoot2->right);

}
bool has_subtree(TreeNode* pRoot1,TreeNode* pRoot2)
{
   bool result=false;
   if(pRoot1&&pRoot2)
   {
     if(pRoot1->val==pRoot2->val)
      {
        result=is_subTree(pRoot1,pRoot2);
       }
     if(!result)
        result=has_subtree(pRoot1->left,pRoot2);
     if(!result)
        result=has_subtree(pRoot1->right,pRoot2);
    }
    return result;
}

10.操作给定的二叉树,将其变换为源二叉树的镜像。

二叉树的镜像定义:源二叉树 
    	    8
    	   /  \
    	  6   10
    	 / \  / \
    	5  7 9 11
    	镜像二叉树
    	    8
    	   /  \
    	  10   6
    	 / \  / \
    	11 9 7  5
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
         if(!pRoot)
             return;
         TreeNode* temp=pRoot->left;
         pRoot->left=pRoot->right;
         pRoot->right=temp;
        if(pRoot->left)
            Mirror(pRoot->left);
        if(pRoot->right)
            Mirror(pRoot->right);
    }
};

11.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出是的,否则输出号假设输入的数组的任意两个数字都互不相同。

答:

class Solution {
public:
    bool v_bst(int* arr,int length)
    {
        if(length<=0)
            return false;
        int root=arr[length-1];
        int i=0;
        for(;i<length-1;i++)
            if(arr[i]>root)
                break;
        int j=i;
        for(;j<length-1;j++)
            if(arr[j]<root)
                return false;
       bool  left=true;
            if(i>0)        
                left=v_bst(arr,i);
       bool right=true; 
            if(i<length-1)
                right=v_bst(arr+i,length-i-1);
        return (left&&right);
    }
    bool VerifySquenceOfBST(vector<int> sequence) 
        {
            if(sequence.empty())
                return false;
            unsigned int length=sequence.size();
            int *arr=new int[length];
            bool res=v_bst(arr,length);
            cout<<(res?"Yes":"No");
           return res;
        }
};

12.输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径(注意:在返回值的列表中,数组长度大的数组靠前)。

答:

class Solution {
public:
    void pre_order(TreeNode* root,int currentNumber,int expectNumber)
    {
        if(!root)
            return;
        sta.push_back(root->val);
        currentNumber+=root->val;
        bool leaf=(!(root->left)&&(!root->right));
        if(currentNumber==expectNumber&&leaf)
            vec.push_back(sta);
        if(root->left)
            pre_order(root->left,currentNumber,expectNumber);
        if(root->right)
            pre_order(root->right,currentNumber,expectNumber);
        sta.pop_back();
    }
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        if(!root)
            return vector<vector<int> >();
         pre_order(root,0,expectNumber);
        return vec;
    }
private:
    vector<vector<int> > vec;
    vector<int> sta;
};

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

答:题目提到了已排好序,那么二叉排序树排好序的情况是中序遍历的时候,所以我们可以中序遍历一趟二叉搜索树,然后入队。然后通过不断出队。当前节点的右孩子指向队头。左孩子指向前一个出队结点使用pre来指向,每次循环不断更新这个pre。

class Solution{
public:
	void inorder(TreeNode* pRootOfTree)
	{
		if (!pRootOfTree)
			return;
		inorder(pRootOfTree->left);
		q.push(pRootOfTree);
		inorder(pRootOfTree->right);
	}
	TreeNode* Convert(TreeNode* pRootOfTree)
	{
		if (!pRootOfTree)
			return nullptr;
		inorder(pRootOfTree);
		TreeNode* Head = q.front();
		TreeNode* pre = nullptr;
		while (!q.empty())
		{
			TreeNode* temp = q.front();
			temp->left = pre;
			q.pop();
			if (!q.empty())
				temp->right = q.front();
			else
				temp->right = nullptr;
			pre = temp;
		}
		return Head;
	}
private:
	queue<TreeNode*> q;
};

14.给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

答:其实就是求二叉树的中序遍历序列的后继:

    1.如果有右子树则右子树的最左孩子就是后继如果该右孩子没有左孩子那么后继就是这个该右孩子。

    2.如果没有右孩子,如果当前节点是其父母节点的左孩子节点的话,那么后继是其父母。如果是其父母的右孩子的话,就延父母节点上溯,直到找到当前节点是其父母节点的左孩子。此刻该父母节点为后继。

前驱:

    1.如果当前节点有左子树的话,前驱为该节点左子树的最右节点。如果左子树没有右孩子,则前驱就是该左孩子。

    2.如果当前节点没有左孩子的话当前节点是其父母节点的右孩子,则前驱就是父母节点。如果当前节点是其父母节点的左孩子的话,延父母节点继续上溯,直到找到当前节点是其父母节点的右孩子,则该父母节点为前驱。

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(!pNode)
            return nullptr;
        if(pNode->right)
        {
            TreeLinkNode* temp=pNode->right;
            while(temp->left)
            {
                temp=temp->left;
            }
            return temp;
        }else
        {
            TreeLinkNode* par=pNode->next;
            while(par&&pNode==par->right)
            {
                pNode=par;
                par=par->next;
            }
            return par;
        }
    }
};

15.请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

答:如何判断一个二叉树是对称的,我们从左边遍历,和从右边遍历应该值是相等的。

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
       return isSymmetrical(pRoot,pRoot);
    }
    bool isSymmetrical(TreeNode* pRoot1,TreeNode* pRoot2)
    {
        if(!pRoot1&&!pRoot2)
            return true;
        if(!pRoot1||!pRoot2)
            return false;
        if(pRoot1->val!=pRoot2->val)
            return false;
        return isSymmetrical(pRoot1->left,pRoot2->right)&&isSymmetrical(pRoot1->right
                                                                       ,pRoot2->left);
    }
};

16.请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

答:与之前按行遍历的思路类似,如果我们要按之子形打印二叉树的话,我们可以定义一个标志位以便用来表示此时就从左到右打印还是从右到左打印那么如何实现从左到右打印或者从右到左打印呢?队列和栈。我们在扫描每一层的时候,元素同时入队入栈。当选择打印方式时,通过那个标志位来选择。如果从左到右打印就选择队列。从右到左打印就选择栈。需要注意的就是每次其中一个容器打印完,另一个容器需要清空。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        if(!pRoot)
            return vector<vector<int> >();
        vector<vector<int> > vec;
        vector<int> vt;
        TreeNode* last=pRoot;
        TreeNode* nlast=nullptr;
        int order=true;//order为true从左到右打印
        queue<TreeNode*> q;
        queue<TreeNode*> tem;
        stack<TreeNode*> sta;
        q.push(pRoot);
        while(!q.empty())
        {
            TreeNode* temp=q.front();
            q.pop();
            sta.push(temp);
            tem.push(temp);
            if(temp->left)
            {
                q.push(temp->left);
                nlast=temp->left;
            }
            if(temp->right)
            { 
                q.push(temp->right);
                nlast=temp->right;
            }
            if(last->left==nlast||last->right==nlast||temp==last)
            {
                last=nlast;
                if(order)
                {
                    while(!tem.empty())
                    {
                        vt.push_back(tem.front()->val);
                        tem.pop();
                    }
                     while(!sta.empty())
                    {
                        sta.pop();
                    }
                    order=false;
                }else
                {
                    while(!sta.empty())
                    {
                        vt.push_back(sta.top()->val);
                        sta.pop();
                    }
                   while(!tem.empty())
                    {
                        tem.pop();
                    }
                    order=true;
                }
                vec.push_back(vt);
                vt.clear();
            }
        }
        return vec;
    }
    
    
};

17.请实现两个函数,分别用来序列化和反序列化二叉树

class TreeToString {
public:
string getString(int x){
		string ret;
		while(x) ret += '0' + (x % 10),x /= 10;
		reverse(ret.begin(),ret.end());
		return ret;
	}
	void convert(TreeNode* root,string &ret){
		if(root == NULL){
			ret = ret + "#!";
			return;
		}
		ret = ret + getString(root->val) + "!";
		convert(root->left,ret);
		convert(root->right,ret);
	}
    string toString(TreeNode* root) {
		string ret;
		convert(root,ret);
		return ret;
    }
};

18.给定一棵二叉搜索树,请找出其中的第ķ小的结点。例如,(5,3,7,2,4,6,8)中,按结点数值大小顺序第三小结点的值为4。

答:中序遍历序列本身就是有序的,所以我们只要不断遍历,如果当前节点为第ķ个的话,就把它标注。

class Solution {
public:
    void inorder(TreeNode* pRoot,TreeNode* &ans)
    {
        if(!pRoot)
            return;
        inorder(pRoot->left,ans);
        count--;
        if(!count) ans=pRoot;
        inorder(pRoot->right,ans);
    }
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(!pRoot||k<1)
            return nullptr; 
        count=k;
        TreeNode* ans=nullptr;
        inorder(pRoot,ans);
        return ans;
    }

private: 
    int count=0;
};

19.请把纸条竖着放在桌浑上,然后从纸条的下边向上椹对折,压出折痕后再展开。此时有1条折痕,突起的蚂向指向纸条的背Ÿ ,这条折痕叫做“下”折痕;突起的⽅向指向纸条正Ÿ的折痕叫做“上”折痕如果每次都从下边向上对折,对折ñ次请从上到下计算出所有折痕的⽅向。

给定折的次数n,请返回从上到下的折痕的数组,若为下折痕则对应元素为“down”,若为上折痕则为“up”。

测试样例:

1
返回:["down"]

20.一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请找到这两个错误节点并返回他们的值。保证二叉树中结点的值各不相同。

给定一棵树的根结点,请返回两个调换了位置的值,其中小的值在前。

21.从二叉树的节点甲出发,可以向上或者向下走,但沿途的节点只能经过一次,当到达节点乙时,路径上的节点数叫作甲到乙的距离。对于给定的一棵二叉树,求整棵树上节点间的最大距离。

给定一个二叉树的头结点root,请返回最大距离。保证点数大于等于2小于等于500。

22.有一棵二叉树,其中所有节点的值都不一样,找到含有节点最多的搜索二叉子树,并返回这棵子树的头节点。

给定二叉树的头结点root,请返回所求的头结点,若出现多个节点最多的子树,返回头结点权值最大的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值