博客园  :: 首页  :: 新随笔  :: 管理

3.二叉树

Posted on 2021-03-29 21:46  wsg_blog  阅读(109)  评论(0编辑  收藏  举报

Index LeetCode

树作为单链表的升级版,我们通常接触的树都是二叉树(binary tree),即每个节点最多有两个子节点;可以看出,其与链表的主要差别就是多了一个子节点的指针。

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

二叉树的遍历
在对二叉树的大部分操作中,我们会大量的递归,那么什么是递归?递归一般使用于树的循环遍历,类似于数组中while(i++ < nums.size()),为防止递归进入死循环,必须要有递归结束条件;另一种就是层次遍历一般使用queue<TreeNode*> q及双while固定结构;通常二叉树的遍历方式为以上两种,即:递归和层序,其中递归遍历的通用性更强一些,但不易理解。
特殊二叉树
二叉搜索树:左子树的值 < 各根节点 < 右子树的值,即存在中序遍历有序的性质。
完全二叉树:叶子节点只能出现在最下层,且最下层的叶子节点集中在树的左侧,层序遍历最下层,从左至右连续有值。
平衡二叉树:任意节点两边子树深度相差是否绝对值小于等于1,递归每层左右节点深度值,求差值判断。
二叉树的重建及序列化反序列化

BM23.二叉树的前序遍历(144.二叉树的前序遍历)[easy]

输入:root=[1,null,2,3]
输出:[1,2,3]
递归

vector<int> preorderTraversal(TreeNode* root){
  vector<int> res;
  traversal(root, res);
  return res;
}
void traversal(TreeNode* root, vector<int>& vec){
  if(root==nullptr) return;
  vec.push_back(root->val);
  traversal(root->left, vec);
  traversal(root->right, vec);
}
BM24.二叉树的中序遍历(94.二叉树的中序遍历)[easy]

输入:root=[1,null,2,3]
输出:[1,3,2]
递归

vector<int> inorderTraversal(TreeNode* root) {
  vector<int> res;
  traversal(root, res);
  return res;
}
void traversal(TreeNode* root, vector<int>& vec){
  if(root==nullptr) return;
  traversal(root->left, vec);
  vec.push_back(root->val);
  traversal(root->right, vec);
}
BM25.二叉树的后序遍历(145.二叉树的后续遍历)[easy]

输入:root=[1,null,2,3]
输出:[3,2,1]
递归

vector<int> postorderTraversal(TreeNode* root) {
  vector<int> res;
  traversal(root, res);
  return res;
}
void traversal(TreeNode* root, vector<int>& vec){
  if(root==nullptr) return;
  traversal(root->left, vec);
  traversal(root->right, vec);
  vec.push_back(root->val);
}
BM26.求二叉树的层序遍历(102.二叉树的层序遍历)[medium]

输入:root=[3,9,20,null,null,15,7]
输出:[[]3,[9,20],[15,7]]
层序遍历:queue<TreeNode*> q ;

vector<vector<int>> levelOrder(TreeNode* root){
  vector<vector<int>> res;
  if(root==nullptr) return res;
  queue<TreeNode*> q;
  q.push(root);
  while(!q.empty()){
    int s=q.size();
    vector<int> row;
    while(s--){
      TreeNode* node=q.front();
      q.pop();
      row.push_back(node->val);
      if(node->left) q.push(node->left);
      if(node->right) q.push(node->right);
    }
    res.push_back(row);
  }
  return res;
}
BM27.按之字形顺序打印二叉树(103.二叉树的锯齿形层次遍历)[medium]

输入:root=[3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
层次遍历

vector<vector<int>> zigzagLevelOrder(TreeNode* root){
  vector<vector<int>> res;
  if(root==nullptr) return res;
  queue<TreeNode*> q;
  q.push(root);
  bool flag=true;
  while(!q.empty()){
    falg=!flag;
    vector<int> row;
    int s=q.size();
    while(s--){
      TreeNode* node=q.front();
      q.pop();
      row.push_back(node->val);
      if(node->left) q.push(node->left);
      if(node->right) q.push(node->right);
    }
    if(falg) reverse(row.begin(), row.end());
    res.push_back(row);
  }
  return res;
}
BM28.二叉树的最大深度(104.二叉树最大深度)[easy]

输入:root=[3,9,20,null,null,15,7]
输出:3
递归或层次遍历

//递归
int maxDepth(TreeNode* root){
  if(!root) return 0;  //空节点没有深度
  return max(maxDepth(root->left), maxDepth(root->right))+1;  //返回子树深度+1
}
//层次遍历
int maxDepth(TreeNode* root){
  if(!root) return 0;
  queue<TreeNode*> q;
  q.push(root);
  int res=0;
  while(!q.empty()){
    int s=q.size();
    while(s--){
      TreeNode* node=q.front();
      q.pop();
      if(node->left) q.push(node->left);
      if(node->right) q.push(node->right);
    }
    res++;
  }
  return res;
}
BM29.二叉树中和为某一值的路径(一)(112.路径总和)[medium]

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
递归

bool hasPathSum(TreeNode* root, int targetSum){
  if(!root)
    return false;  //空节点找不到路径
  if(root->left==nullptr && root->right==nullptr && targetSum-root->val==0)
    return true;    //叶子节点,且路径和为sum
  return hasPathSum(root->left, targetSum-root->val) || hasPathSum(root->right, targetSum-root->val);
}

BM30.二叉搜索树与双向链表(剑指Offer46.二叉搜索树与双向链表)[medium]

输入:二叉搜索树(中序遍历有序)
输出:排序的循环双链表
递归中序遍历
将二叉搜索树转化成递增序的双向链表;不能添加新的节点,要在原始节点的基础上添加链表链接;返回链表中的第一个节点的指针;二叉树节点的左右指针堪称双向链表的前后指针

TreeNode* head=nullptr;  //返回的第一指针,即为最小值,先定为null
TreeNode* pre=nullptr;   //中序遍历当前值的上一位,初值为最小值,先定为null
TreeNode* Convert(TreeNode* pRootOfTree){
  if(pRtootOfTree==nullptr)
    return nullptr;
  Convert(pRootOfTree->left); //首先递归到最左最小值
  if(pre == nullptr){  //找到最小值,初始化head与pre
    head=pRootOfTree;
    pre=pRootOfTree;
  }else{    //当前节点与上一节点建立连接,将pre设置为当前值
    pre->right=pRootOfTree;
    pRootOfTree->left=pre;
    pre=pRootOfTree;
  }
  Convert(pRootOfTree->right);
  return head;
}
BM31.对称的二叉树(101.对称二叉树)[easy]

输入:root=[1,2,2,3,4,4,3]
输出:true
递归

bool isSymmetric(TreeNode* root){
  if(!root) return false;
  return dfs(root->left, root->right);
}
bool dfs(TreeNode* t1, TreeNode* t2){
  if(t1==nullptr && t2==nullptr) return true;
  if(t1==nullptr || t2==nullptr) return false;
  if(t1->val != t2->val) return false;
  return dfs(t1->left, t2->right) && dfs(t1->right, t2->left);
}
BM32.合并二叉树(617.合并二叉树)[easy]

输入:root1=[1,3,2,5],root2=[2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]
递归,自顶向下

TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2){
  if(root1 == nullptr) return root2;
  if(root2 == nullptr) return root1;
  root1->val = root1->val+root2->val;
  root1->left = mergeTrees(root1->left, root2->left);
  root1->right = mergeTrees(root1->right, root2->right);
  return root1;
}
BM33.二叉树的镜像(剑指Offer27.二叉树的镜像)[easy]

输入:root=[4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
递归,从顶向下,交换左右指针值

TreeNode* mirrorTree(TreeNode* root){
  if(root == nullptr) return nullptr;
  swap(root->left, root->right);
  mirrorTree(root->left);
  mirrorTree(root->right);
  return root;
}
BM34.判断是不是二叉搜索树(98.验证二叉搜索树)[medium]

输入:root=[5,1,4,null,null,3,6]
输出:false
说明:二叉搜索树,左子树只包含小于当前节点,右子树只包含大于当前节点
中序遍历递增<==>二叉搜索树

long pre=LONG_MIN;
bool isValidBST(TreeNode* root){
  if(root == nullptr) return true;
  if(!isValidBST(root->left)) return false;  //中序遍历 先进入左子树
  if(pre >= root->val) return false;
  pre=root->val;
  return isValidBST(root->right);  //在进入右子树
}
BM35.判断是不是完全二叉树()[medium]

输入:root=[1,2,3,4,5,6],root=[1,2,3,4,5,null,6]
输出:ture,false
说明:完全二叉树叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在树的左部。
层次遍历

bool isCompleteTree(TreeNode* root){
  if(root == nullptr) return true;
  queue<TreeNode*> q;
  q.push(root);
  bool falg = false;  //标记第一次出现空节点
  while(!q.empty()){  //层序遍历
    int s=q.size();
    while(s--){
      TreeNode* node=q.front();
      q.pop();
      if(node == nullptr) flag=true;    //标记第一次出现空节点
      else{
        if(flag) return false;        //不是完全二叉树
        q.push(node->left);
        q.push(node->right);
      }
    }
    return true;
  }
}
BM36.判断是不是平衡二叉树(110.平衡二叉树)[easy]

输入:root=[3,9,20,null,null,15,7] root=[1,2,2,3,3,null,null,4,4]
输出:true,false
说明:平衡二叉树任意一节点两边子树深度相差是否绝对值小于等于1
递归,自顶向下

bool isBalanced(TreeNode* root){
  if(!root) return true;
  int left = deepTree(root->left);  //左子树深度
  int right = deepTree(root->right);  //右子树深度
  if(1 < left-right || left-right < -1) return false;
  return isBalanced(root->left) && isBalanced(root->right);
}
int deepTree(TreeNode* root){    //二叉树的最大深度
  if(!root) return 0;
  return max(deepTree(root->left), deepTree(root->right))+1;
}
BM37.二叉搜索树的最近公共祖先(235.二叉搜索树的最近公共祖先)[easy]

输入:root=[6,2,8,0,4,7,9,null,null,3,5],p=2,q=8
输出:6
说明:给定一个二叉搜索树(有序),找到该树中两个指定节点的最近公共祖先,(一个节点也可以是它自己的祖先)
找到到达p、q的路径,两路径相交的最后一个节点即最近公共祖先

vector<int> getPath(TreeNode* root, int target){
  vector<int> path;
  TreeNode* node=root;
  while(node->val != target){
    path.push_back(node->val);
    if(node->val > target)
      node=node->left;
    else
      node=node->right;
  }
  path.push_back(target);
  return path;
}
TreeNode* lowestCommonAncestor(TreeNode* root, int p, Tint q){
  if(root == nullptr || root == p || root == q)
    return root;
  vector<int> path_p, path_q;
  path_p=getPath(root, p);
  path_q=getPath(root, q);
  int res=0;
  for(int i=0; i<path_p.size() && i<path_q.size(); i++){
    if(path_q[i]==path_p[i])
      res=path_p[i];
    else
      break;
  }
  return res;
}
BM38.在二叉树中找到两个节点的最近公共祖先(236.二叉树的最近公共祖先)[medium]

输入:root=[3,5,1,6,2,0,8,null,null,7,4],p=5,q=1
输出:3
给定一个二叉树,找到该树中两个指定节点的最近公共祖先,(一个节点也可以是它自己的祖先)
和搜索二叉树最先类似,这个找路径的时候用 回溯

bool flag=false;  //记录是否找到o的路径
void dfs(TreeNode* root, vector<int>& path, int o){  //求得根节点到目标节点的路径
  if(flag || root==nullptr) return;
  path.push_back(root->val);
  if(root->val == o){
    flag=true;
    return;
  }
  dfs(root->left, path, o);
  dfs(root->right, path, o);
  path.pop_back();
}
int lowestCommonAncestor(TreeNode* root, int o1, int o2){
  vector<int> path1, path2;
  dfs(root, path1, o1);
  flag=false;
  dfs(root, path2, o2);
  int res;
  for(int i=0; i<path1.size() && i<path2.size(); i++){
    if(path1[i]==path2[i]){
      res=path1[i];
    }else{
      break;
    }
  }
  return res;
}
BM39.序列化二叉树(297.二叉树的序列化与反序列化)[hard]

输入:root=[1,2,3,null,null,4,5]
输出:[1,2,3,null,null,4,5]
说明:只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构
前序遍历

string serialize(TreeNode* root){
  if(root==nullptr) return string();
  stringstream ss;    //字节比特序列化
  dfs(root, ss);
  return ss.str();
}
void dfs(TreeNode* root, stringstream& ss){
  if(root==nullptr){
    ss << "# ";    //标识叶子节点的左右为空
    return;    
  }
  ss << to_string(root->val) << " ";  //解码分隔符 “空格”
  dfs(root->left, ss);
  dfs(root->right, ss);
}

TreeNode* deserialize(string data){
  if(data.empty()) return nullptr;
  TreeNode* root=nullptr;
  stringstream ss(data);
  rebuild(root, ss);
  return root;
}
void rebuild(TreeNode* & root, stringstream& ss){  //注意这里 TreeNode*   &   root,
  string t;
  ss >> t;    //空格
  if(t[0] == '#'){
    root=nullptr;
    return;
  }
  int v = stoi(t);
  root=new TreeNode(v);
  rebuild(root->left, ss);
  rebuild(root->right, ss);
}
BM40.重建二叉树(剑指Offer07.重建二叉树)[medium]

输入:preorder=[3,9,20,15,7], inorder=[9,3,15,20,7]
输出:[3,9,20,null,null,15,7]
说明:preorder为前序遍历,inorder为中序遍历,无重复元素

unordered_map<int, int> index;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder){
  for(int i=0; i<inorder.size(); i++)
    index[inorder[i]]=i;  //把中序遍历按值做成索引
  return myBuildTree(preoder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
}
TreeNode* myBuildTree(vector<int>& preorder, vector<int>& inorder, 
          int pre_strat, int pre_end, int in_start, int in_end){
  if(pre_start > pre_end) return nullptr;
  int root_val=preorder[pre_start];
  int rootin_index=index[root_val];
  int leftpre_size=rootin_index-in_start;
  TreeNode* root=new TreeNode(root_val);
  root->left=myBuildTree(preoder, inorder, pre_start+1, pre_start+leftpre_size, in_start, rootin_index-1);
  root->right=myBuildTree(preorder, inorder, prestart+1+leftpre_size, pre_end, rootin_index+1, in_end);
  return root;
}
BM41.输出二叉树的右视图(199.二叉树的右视图)[medium]

输入:[1,2,3,null,5,null,4]
输出:[1,3,4]
层序遍历,打印最右节点值

vector<int> rightSideView(TreeNode* root) {
  vector<int> res;
  if(root==nullptr) return res;
  queue<TreeNode*> q;
  q.push(root);
  while(!q.empty()) {
    int s=q.size();
    while(s--){
      TreeNode* node=q.front();
      q.pop();
      if(s==0) res.push_back(node->val);
      if(node->left) q.push(node->left);
      if(node->right) q.push(node->right);
    }
  }
  return res;
}