一、根据二叉树创建字符串
题目:


思路:这个题很明显需要我们采用二叉树的递归实现(前序遍历),但有一个注意的点:空括号能不能省略的问题,其实我们发现只要左为空,右不为空不能省略括号,剩下情况均可省略。
代码如下:
class Solution {
public:
    string tree2str(TreeNode* root) {
       string str;
       if(root==nullptr)
       {
        return str;
       }
       str+=to_string(root->val);//转换字符串函数
       //左不为空不能省略括号
       //左空右不空同样也不能省略(反映树的关系)
       //只有左右都空可省略 
       if(root->left||root->right) 
       {
          str+="(";
          str+=tree2str(root->left);
          str+=")";
       }
       //只要右为空即省略
       if(root->right)
       {
        str+="(";
        str+=tree2str(root->right);
        str+=")";
       }
       return str;
    }
};二、二叉树的分层遍历1
题目:给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

这个与我们前面的二叉树初阶部分的层序遍历相似,但不同的是,它要把每层数据分别存储成一个二维数组,我们之前的思路是创建两个队列,分别存储该节点及其孩子,等该节点pop掉又会把他们的孩子带进来。
本题思路:创建一个vector<vector<int>>,再创建一个队列以及变量levelsize(记录每层多少个数据),队列存储的是该节点的指针(并不是该节点数据)每次pop掉以后插入到vector变量中,最后把每次的vector 插入到vector<vector<int>>即可。
代码如下:
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*>q;
        int levelsize=0;
        if(root)
        {
            q.push(root);
            levelsize=1;
        }
        while(levelsize>0)
        {
           //一层一层出
            vector<int>v;
            while(levelsize--)
            {
                
                TreeNode* front=q.front();
                q.pop();
                v.push_back(front->val);
               //下一层的孩子入队列
                if(front->left)
                {
                   q.push(front->left);
                }
                if(front->right)
                {
                   q.push(front->right);
                }
                
                
            }
            vv.push_back(v);//一层层的插入
            levelsize=q.size();//下一层孩子的节点个数
        }
        return vv;
    }
};三、二叉树的分层遍历2
题目,与分层遍历1相同,只不过是从底向上遍历。
思路:在二的代码的基础上逆置一下即可,即在return vv前加一个reverse(vv.begin(),vv.end());代码略
四、二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

比如:5和4的公共祖先是5(自己也可以是祖先),6和4的公共祖先是5,2和8的公共祖先是3,我们能够看出一个规律:除了自己是自己祖先的情况下,剩下的情况一个在祖先的左子树,一个在右子树(只有最近祖先满足这个规律!)
代码如下:
class Solution {
public:
    bool isintree(TreeNode* t, TreeNode* x)//判断是否在子树中
    {
            if(t==nullptr)
            return false;
            return t==x||isintree(t->left,x)||isintree(t->right,x);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr)
        {
          return nullptr;
        }
        if(root==p||root==q)//如果自己就是祖先直接返回即可
        {
            return root;
        }
        bool pinleft=isintree(root->left,p);
        bool pinright = !pinleft;
        bool qinleft=isintree(root->left,q);
        bool qinright = !qinleft;
        if((pinleft&&qinright)||(qinleft&&pinright))//孩子分别在祖先的两侧就是祖先
        {
            return root;
        }
        else if(pinleft&&qinleft)//右子树没有了直接去左子树找形成新的树
            return lowestCommonAncestor(root->left,p,q);
        else
             return lowestCommonAncestor(root->right,p,q);
    }
};虽然运行时结果都正确,但时间复杂度太高了,不太满足实际。
我们提供另一个思路:如果能求出两个结点到根的路径,那么就可以转换成链表相交问题,两个链表的交点就是公共祖先。我们用一个两个栈即可。代码如下:
class Solution {
public:
     bool getpath(TreeNode* root, TreeNode* x,stack<TreeNode*>&path)
     {
        if(root==nullptr)
        return false;
        //该节点不为空,先入栈再判断
        path.push(root);
        if(root==x)
        {
            return true;
        }
        //子树问题
        if(getpath(root->left,x,path))
        {
            return true;
        }
        if(getpath(root->right,x,path))
        {
            return true;
        }
        //走到这里说明其左右子树都不符合,即出栈递归
        path.pop();
        return false;
     }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack <TreeNode*> ppath,qpath;
        getpath(root,p,ppath);
        getpath(root,q,qpath);
//让长的先走,对齐
        while(ppath.size()!=qpath.size())
        {
            if(ppath.size()>qpath.size())
            {
                ppath.pop();
            }
            else
            {
                qpath.pop();
            }
        }
//一起走,找节点
        while(ppath.top()!=qpath.top())
        {
            ppath.pop();
            qpath.pop();
        }
        return ppath.top();
        
    }
};五、二叉搜索树转换成双向链表

我们提供一个简单的思路:中序遍历二叉树,将二叉树的结点存在vector容器中,再把结点的链接关系进行更改即可。在此我们就不用代码演示了。
我们来看下一种思路:依旧是中序遍历二叉树,遍历过程中修改左指针为前驱和右指针为后继指针。记录一个cur和prev,cur为当前中序遍历到的结点,prev为上一个中序遍历的结点,cur->left=prev,但我们不知道cur->right指向哪里,但我们可以让prev->right=cur,也就是说,每个结点的左是再中遍历到当前结点时修改指向前驱,但当前结点的右,是在遍历到下一个结点才可修改后继的。
代码如下:
  
class Solution {
public:
void inorderconvert(TreeNode*cur,TreeNode*&prev)
{
	if(cur==nullptr)
	return ;
	inorderconvert(cur->left,prev);
//中序cur
//左为前驱
	cur->left=prev;
//右为后驱
	if(prev)
	prev->right=cur;
	prev=cur;
	inorderconvert(cur->right,prev);
}
    TreeNode* Convert(TreeNode* root) {
        if(root==nullptr)
		return nullptr;
		TreeNode* prev=nullptr;
		inorderconvert(root, prev);
		TreeNode*head=root;
		while(head->left)
		{
			head=head->left;
		}
//循环链表,头尾相接
		head->left=prev;
		prev->right=head;
		return head;
    }
};
我们发现,以上所有的题目貌似都是用递归的思想去实现代码的,接下来我们来实现一下非递归的思想。
六、二叉树的前、中、后序遍历(非递归)
前序:先访问左路结点,再访问左路结点的右子树,访问右子树要以循环从栈依次取出这些结点,循环子问题的思想访问左路结点的右子树,所以我们需要用一个栈和循环实现。
代码如下:
  
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
       stack<TreeNode*>st;
       vector<int> v;
       TreeNode*cur=root;
       while(cur||!st.empty())
       {
        //每次循环代表访问一棵树的开始
        while(cur)
        {
            v.push_back(cur->val);
            st.push(cur);
            cur=cur->left;
        }
        //左走到头了找出最后一个左的右开始访问
        TreeNode* top=st.top();
         st.pop();
         //循环访问右子树
         cur=top->right;
       }
       return v;
    }
};中序:与前序类似,只是我们先入栈不访问,等到左子树走完了以后我们再进行访问,只需要在前序的代码上稍作修改即可

后序:与之前相比麻烦一些,取到一个左路结点时,左子树是否已经访问过了呢?需要我们进行区分,否则程序会进入死循环,如果左路结点的右子树不为空,右子树没有访问,那么上一个访问节点是左子树的根,如果左路节点右子树不为空,右子树已经访问过了,那么上一个访问的是右子树的根。
代码如下:
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
       stack<TreeNode*>st;
       vector<int> v;
       TreeNode*prev=nullptr;
       TreeNode*cur=root;
       while(cur||!st.empty())
       {
        //每次循环代表访问一棵树的开始
        while(cur)
        {
            st.push(cur);
            cur=cur->left;
        }
       //取一个左路节点的右子树出来访问,这时代表左路节点的左子树已经访问过了
        TreeNode* top=st.top();
         //右子树为空或者上一个访问的节点是右子树的根,代表右子树也访问过了。
         if(top->right==nullptr||top->right==prev)
         {
          v.push_back(top->val);
          st.pop();
           prev=top;
         }
         else
         {
         cur=top->right;
          }
    }return v;
};![[JavaEE] 网络初识(网络通信 及 TCP / UDP / IP 封装分用 )](https://i-blog.csdnimg.cn/direct/ae397e2303c34179beff6cbdd84e5979.png)













![[001-02-001].第2节:java开发环境搭建](https://i-blog.csdnimg.cn/blog_migrate/ed04a8ae5c3cfe161e69b986c024486e.png)




