算法总结归纳(第三天)(普通二叉树(非搜索树)总结)

目录

一、二叉树三种遍历(深度优先搜索)

Ⅰ、前序遍历(中左右)

①、递归遍历

②、迭代遍历

Ⅱ、后序遍历(左中右)

①、递归遍历

②、迭代遍历

Ⅲ、中序遍历(左右中)

①、递归遍历

②、迭代遍历

二、二叉树层序遍历(广度优先搜索)

①、例题1

②、例题2

③、例题3

④、例题4

三、二叉树的属性

Ⅰ、对称二叉树

Ⅱ、二叉树的最大深度

①、层序遍历解法

②、递归遍历解法

Ⅲ、二叉树最小深度

Ⅳ、完全二叉树的节点个数

①、层序遍历

②、递归遍历

Ⅴ、平衡二叉树

Ⅵ、二叉树所有路径

Ⅶ、左叶子之和

 Ⅷ、找树左下角的值

①、层序遍历

②、递归遍历

Ⅸ、路径总和

①、目标值减法求解

②、路径加法求解

四、二叉树的修改与构造

1、翻转二叉树

①、递归法

②、迭代法

2、通过两种遍历方式构造第三种二叉树的类型

①、中序与后序遍历构造二叉树

②、前序与中序遍历构造二叉树

③、*前序列与后序构造(无法构造)

五、最大二叉树

六、合并二叉树


一、二叉树三种遍历(深度优先搜索)

Ⅰ、前序遍历(中左右)

①、递归遍历

题目链接:二叉树前序遍历

我们只需要确定中左右的处理逻辑,然后中、左、右递归处理即可。

void backtracking(TreeNode* root, vector& res)
{
    if(root == NULL) return;
    res.push_back(root -> val);
    if(root ->left) backtracking(root ->left, res);
    if(root ->right) backtracking(root ->right, res); 
}

    vector preorderTraversal(TreeNode* root) {
        vector res;
       if(root == NULL) return res;
       backtracking(root, res);
       return res; 
    }

②、迭代遍历

题目和上面的题目是一样的。

此处我们利用了栈的特点,实现了二叉树的迭代遍历,需要特别注意的是,当我们遍历的时候,入栈顺序应该是先右后左,栈有后进先出的特点。这样,我们就可以保证先存储左,后右,实现前序遍历。

ector preorderTraversal(TreeNode* root) {
        vector res;
        if(root == NULL) return res;
        stack st;
        st.push(root);
        while(!st.empty())
        {
            TreeNode* tem = st.top();
            st.pop();
            res.push_back(tem ->val);
            if(tem ->right) st.push(tem->right);
            if(tem ->left) st.push(tem ->left);
        }
        return res;
    }

Ⅱ、后序遍历(左中右)

①、递归遍历

题目链接:二叉树后序遍历

遍历顺序左右中,切记要清楚每种遍历的顺序,这很重要。

void backtracking(TreeNode* root, vector& res)
{
    if(root == NULL) return;
    if(root ->left) backtracking(root ->left, res);
    if(root ->right) backtracking(root ->right, res);
    res.push_back(root ->val);
}

    vector postorderTraversal(TreeNode* root) {
        vector res;
        if(root == NULL) return res;
        backtracking(root, res);
        return res;
    }

②、迭代遍历

题目与上面一样。

我们根据中序遍历的代码为中左右,后序遍历为左右中,那么我们只需要先写出中右左,然后reverse一下就得到左右中。

 vector postorderTraversal(TreeNode* root) {
        vector res;
        if(root == NULL) return res;
        stack st;
        st.push(root);
        while(!st.empty())
        {
            TreeNode* tem = st.top();
            st.pop();
            res.push_back(tem ->val);
            if(tem ->left) st.push(tem ->left);
            if(tem ->right) st.push(tem ->right);
        }
        reverse(res.begin(), res.end());
        return res;
    }

Ⅲ、中序遍历(左右中)

①、递归遍历

题目链接:二叉树中序遍历

切记遍历顺序。左右中

void backtracking(TreeNode* root, vector& res)
{
    if(root == NULL) return;
    if(root ->left) backtracking(root ->left, res);
    res.push_back(root ->val);
    if(root ->right) backtracking(root ->right, res);
}

    vector inorderTraversal(TreeNode* root) {
        vector res;
        if(root == NULL) return res;
        backtracking(root, res);
        return res;
    }

②、迭代遍历

题目与上面一样。

此处我们借用了一个指针,从而当指针不为空,一直向左储存,当为空时,说明触底了,然后处理左值并且向右储存。

 vector inorderTraversal(TreeNode* root) {
        vector res;
        if(root == NULL) return res;
        stack st;
        TreeNode* tem = root;
        while(!st.empty() || tem != NULL)
        {
            if(tem != NULL)
            {
                st.push(tem);
                tem = tem ->left;
            }
            else {
                tem = st.top();
                st.pop();
                res.push_back(tem ->val);
                tem = tem ->right;
            }
        }
        return res;
    }

二、二叉树层序遍历(广度优先搜索)

①、例题1

题目链接:二叉树层序遍历

该方法使用的是队列,然后套用一个for循环。这样可以从左到右的数据存储起来。

 vector> levelOrder(TreeNode* root) {
      vector> res;
      if(root == NULL) return res;
      queue que;
      que.push(root);
      while(!que.empty())
      {
          int sz = que.size();
          vector nums;
          for(int i = 0 ;i < sz; i++){
              TreeNode* tem = que.front();
              que.pop();
              nums.push_back(tem ->val);
              if(tem ->left) que.push(tem ->left);
              if(tem ->right) que.push(tem ->right);
          }
          res.push_back(nums);
      }
      return res;
    }

②、例题2

链接:二叉树层序遍历Ⅱ

vector> levelOrderBottom(TreeNode* root) {
        vector> result;
        queue que;
        if(root!=NULL) que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            vector p;
            for(int i = 0;ival);
                if(tem->left) que.push(tem->left);
                if(tem->right) que.push(tem->right);
            }
            result.push_back(p);
        }
        reverse(result.begin(),result.end());
        return result;
    }

③、例题3

题目链接:二叉树右视图

 vector rightSideView(TreeNode* root) {
      vector result;
      queue que;
      if(root == NULL )return result;
      que.push(root);
      while(!que.empty())
      {
          int sz = que.size();
          result.push_back(que.back() ->val);
          for(int i = 0; ileft) que.push(tem ->left);
              if(tem ->right) que.push(tem ->right);
          }
      }
      return result;
    }

④、例题4

题目链接:填充右侧节点

 Node* connect(Node* root) {
       queue que;
       if(root == NULL) return root;
       que.push(root);
       while(!que.empty())
       {
           int size = que.size();
           for(int i = 0;ileft) que.push(tem->left);
               if(tem->right) que.push(tem->right);
               if(i == size-1){
                   tem->next = NULL;
               }
               else{
               tem->next = que.front();
               }
           }
       }
       return root;
    }

三、二叉树的属性

Ⅰ、对称二叉树

题目链接:对称二叉树

这里使用递归遍历,维持的原则上两边树的外侧和外侧比较,内侧和内侧比较,然后总体汇总true和false情况,最后返回。

bool backtracking(TreeNode* leftnode, TreeNode* rightnode)
{
    if(leftnode == NULL && rightnode == NULL) return true;
    else if(leftnode == NULL && rightnode != NULL) return false;
    else if(leftnode != NULL && rightnode == NULL) return false;
    else if(leftnode ->val != rightnode ->val) return false;
    bool left = backtracking(leftnode ->left, rightnode ->right);
    bool right = backtracking(leftnode ->right, rightnode ->left);
    return left && right;

}

    bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        return backtracking(root ->left, root ->right);
    }

Ⅱ、二叉树的最大深度

题目链接:二叉树最大深度

①、层序遍历解法

该解法,就是利用了层序遍历每次遍历完一层,result加1,然后统计总层数,也就是最大深度。

 int maxDepth(TreeNode* root) {
        if(root==NULL) return 0;
        queue que;
        que.push(root);
        int result = 0;
        while(!que.empty())
        {
            int sz = que.size();
            for(int i = 0; ileft) que.push(tem ->left);
                if(tem ->right) que.push(tem ->right);
            }
            result ++;
         }
        return result;
    }

②、递归遍历解法

本题采用前序遍历(中左右)解法。res每次++表示的是没到一层,记录当前层数。tem定义为全局变量,tem用于记录历史最大层数。最后的res--是回溯的处理方式,因为左右都遍历完了,所以当前层数减一。

int tem = 0;
void backtracking(TreeNode* root, int& res)
{
    if(root == NULL) return;
    res ++;
    tem = max(res, tem);
    backtracking(root ->left, res);
    backtracking(root ->right, res);
    res --;
}

    int maxDepth(TreeNode* root) {
        int res = 0;
        backtracking(root, res);
        return tem;
    }

Ⅲ、二叉树最小深度

该代码的重点是递归中判断NULL的条件有所不同。同时分别记录树左右两边的深度,然后选取两者中的最小值 + 1。

int backtracking(TreeNode* root)
{
    if(root == NULL) return 0;
    int leftdepth = backtracking(root ->left);
    int rightdepth = backtracking(root ->right);
    if(root ->left == NULL && root ->right != NULL) return 1 + rightdepth;
    if(root ->left != NULL && root ->right == NULL) return 1 + leftdepth;
    return 1 + min(leftdepth, rightdepth); 
}

    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        return backtracking(root);
    }

Ⅳ、完全二叉树的节点个数

题目链接:完全二叉树的节点个数

①、层序遍历

int countNodes(TreeNode* root) {
       if(root == NULL) return 0;
       stack st;
       st.push(root);
       int res = 0;
       while(!st.empty())
       {
           int sz = st.size();
           for(int i = 0; ileft) st.push(tem ->left);
               if(tem ->right) st.push(tem ->right);
           }
       }
       return res;
    }

②、递归遍历

确定遍历顺序是后序遍历(左右中)。然后左右逻辑执行完之后就一个节点结束。res++。最后

res就表示最终结果。

void backtrackint(TreeNode* root, int& res)
{
    if(root == NULL) return;
    backtrackint(root ->left, res);
    backtrackint(root ->right, res);
    res ++;
}

    int countNodes(TreeNode* root) {
        if(root == NULL) return 0;
        int res = 0;
        backtrackint(root, res);
        return res;
    }

Ⅴ、平衡二叉树

题目链接:平衡二叉树

平衡二叉树定义:左右两个子树的高度差不高于1。

本题我们要知道遍历,为后续遍历,同时要清楚平衡二叉树要满足的所有地方的子树差都小于等于1,所以有一个不符合,我们就直接返回-1。

int backtracking(TreeNode* root)
{
    if(root == NULL) return 0;
    int leftdepth = backtracking(root ->left);
    if(leftdepth == -1) return -1;
    int rightdepth = backtracking(root ->right);
    if(rightdepth == -1) return -1;
    return abs(rightdepth - leftdepth) > 1 ? -1 : 1 + max(rightdepth, leftdepth);
}

    bool isBalanced(TreeNode* root) {
        if(root == NULL) return true;
        return backtracking(root) != -1;
    }

Ⅵ、二叉树所有路径

题目链接:二叉树所有路径

使用递归同时进行回溯的细节处理,就可以很好的完成本题。

void backtracking(TreeNode* root, vector& res, string path)
{
    path += to_string(root ->val);
    if(root ->left == NULL && root ->right == NULL){
        res.push_back(path);
        return;
    }
    if(root ->left){
        path += "->";
        backtracking(root ->left, res, path);
        path.pop_back();path.pop_back();
    }
     if(root ->right){
        path += "->";
        backtracking(root ->right, res, path);
        path.pop_back();path.pop_back();
    }
}

    vector binaryTreePaths(TreeNode* root) {
        vector res;
        string path;
        if(root == NULL) return res;
        backtracking(root, res, path);
        return res;
    }

Ⅶ、左叶子之和

题目链接:左叶子之和

此处注意左叶子并非左侧节点。左叶子必须满足左侧节点不为空,同时左侧节点的左右两边均为空。

int backtracking(TreeNode* root)
{
    if(root == NULL ) return 0;
    if(root ->left == NULL && root ->right == NULL) return 0;
    int leftnode = backtracking(root ->left);
    if(root -> left != NULL && root ->left ->left == NULL && root ->left ->right == NULL){
        leftnode = root ->left ->val;
    }
    int rightnode = backtracking(root ->right);
    return leftnode + rightnode;
}

    int sumOfLeftLeaves(TreeNode* root) {
        if(root == NULL) return 0;
        return backtracking(root);
    }

 Ⅷ、找树左下角的值

题目链接:找树左下角的值

①、层序遍历

此处思路为每次层序遍历时候,i == 0的时候永远是每一行最左边的值,这样,我们就不用担心最左边但不是最后一层的问题了。

int findBottomLeftValue(TreeNode* root) {
        queue que;
        if(root==NULL) return 0;
        if(root->left==NULL&&root->right==NULL) return root->val;
        que.push(root);
        int result = 0;
        while(!que.empty())
        {
            int size = que.size();
            for(int i = 0;ileft) que.push(tem->left);
                if(tem->right) que.push(tem->right);
                if(i==0) result = tem->val;
            }
        }
        return result;
    }

②、递归遍历

res全局变量,为最后结果,maxdepth记录最大深度,为每次判断时候的条件做准备。

int res, maxdepth = -1;

void backtracking(TreeNode* root, int depth)
{
    if(root ->left == NULL && root ->right == NULL)
    {
        if(depth > maxdepth){
            maxdepth = depth;
            res = root ->val;
        }
    }
    if(root ->left){
        depth++;
        backtracking(root ->left, depth);
        depth --;
    }
    if(root ->right){
        depth++;
        backtracking(root ->right, depth);
        depth--;
    }
}

    int findBottomLeftValue(TreeNode* root) {
        if(root == NULL) return 0;
        backtracking(root, 0);
        return res;
    }

Ⅸ、路径总和

题目链接:路径总和

①、目标值减法求解

bool backtracking(TreeNode* root, int count)
{
    if(root ->left == NULL && root ->right == NULL && count == 0) return true;
    if(root ->left == NULL && root ->right == NULL) return false;
    if(root ->left){
        count -= root ->left ->val;
        if(backtracking(root ->left, count)) return true;
        count += root ->left ->val;
    }
    if(root ->right){
        count -= root ->right ->val;
        if(backtracking(root ->right, count)) return true;
        count += root ->right ->val;
    }
    return false;
}

    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return false;
        return backtracking(root, targetSum - root ->val);
    }

②、路径加法求解

此法就是通过左右两边的真值情况,如果有一个为true,直接全部返回true,同时每次遇到一个叶子节点就直接使用for循环求解,然后直接返回相等情况。

bool get_sum(TreeNode* cur,vector& num,int targetSum)
{
    num.push_back(cur->val);
    if(cur->left==NULL&&cur->right==NULL) {
          int tem = 0;
        for(int i = 0;ileft){
        bool l = get_sum(cur->left,num,targetSum);
        if(l == true) return l;
        num.pop_back();
    }
    if(cur->right){
        bool r = get_sum(cur->right,num,targetSum);
        if(r==true) return r;
        num.pop_back();
    }
    return false;
}

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        vector num;
        if(root==NULL) return false;
        if(root->left==NULL&&root->right==NULL) return root->val==targetSum;
        return get_sum(root,num,targetSum);
    }
};

四、二叉树的修改与构造

1、翻转二叉树

题目链接:翻转二叉树

切记使用swap()函数的时候,交换的是节点,而不是节点的值。

①、递归法

TreeNode* invertTree(TreeNode* root) {
        if(root == NULL) return root;
        swap(root ->left, root ->right);
        invertTree(root ->left);
        invertTree(root ->right);
        return root;
    }

②、迭代法

TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        stack st;
        st.push(root);
        while(!st.empty()) {
            TreeNode* node = st.top();              // 中
            st.pop();
            swap(node->left, node->right);
            if(node->right) st.push(node->right);   // 右
            if(node->left) st.push(node->left);     // 左
        }
        return root;
    }

2、通过两种遍历方式构造第三种二叉树的类型

①、中序与后序遍历构造二叉树

题目链接:中序与后序构造二叉树

本题目首先明确,后序为(左右中),中序为(左中右),所以,后序数组的最后一个值肯定是树中的高度最高的。因此将这个值记录下来,然后遍历中序数组,找到这个值的坐标。记录i用于分割左右数组。然后分割时,我们结合二分的思想,在每次分割的时候,要记住,区间开闭情况,我们采用左闭右开的区间(同时注意vector中的end操作取得是最后一个值的后一位)。

TreeNode* backtracking(vector& inorder, vector& postorder)
{
    if(postorder.size() == 0) return NULL;
    int idx = postorder[postorder.size() - 1];
    TreeNode* tem = new TreeNode(idx);
    if(postorder.size() == 1) return tem;
    postorder.resize(postorder.size() - 1);
    int i;
    for( i = 0; i leftinorder(inorder.begin(), inorder.begin() + i);
    vector rightinorder(inorder.begin() + 1 + i, inorder.end());
    vector leftpostorder(postorder.begin(), postorder.begin() + leftinorder.size());
    vector rightpostorder(postorder.begin() + leftinorder.size() , postorder.end());
    tem ->left = backtracking(leftinorder, leftpostorder);
    tem ->right = backtracking(rightinorder, rightpostorder);
    return tem;
}

    TreeNode* buildTree(vector& inorder, vector& postorder) {
        if(inorder.size() == 0 || postorder.size() == 0) return NULL;
        return backtracking(inorder, postorder);
    }

②、前序与中序遍历构造二叉树

题目链接:中序与前序构造二叉树

与上述思路一样,只需要分割好数组即可。

③、*前序列与后序构造(无法构造)

前序为(中左右),后序为(左右中),此时我们只能确定中间的位置,一个在头,一个在尾。但是无法知道中间的顺序。

五、最大二叉树

题目链接:最大二叉树

该解法的思路和中序那里很像,先找到最大值,也就是中间值,然后根据这个分割数组,从而实现

两边构造,思路与上面是相同的。

TreeNode* constructMaximumBinaryTree(vector& nums) {
         TreeNode* tem = new TreeNode(0);
         if(nums.size() == 1){
             tem ->val = nums[0];
             return tem;
         }
       int maxvalue = 0;int maxvalueindex = 0;
       for(int i = 0; i maxvalue){
               maxvalue = nums[i];
               maxvalueindex = i;
           }
       }
       tem ->val = maxvalue;
       if(maxvalueindex > 0){
           vector N1(nums.begin(), nums.begin() + maxvalueindex);
           tem ->left =  constructMaximumBinaryTree(N1);
       }
       if(maxvalueindex < nums.size() - 1){
           vector N2(nums.begin() + maxvalueindex + 1, nums.end());
           tem ->right = constructMaximumBinaryTree(N2);
       }
       return tem;
     }

六、合并二叉树

题目链接:合并二叉树

合并非常简单,重叠就相加,有一个是NULL就返回另一个的值。

 TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(root1==NULL) return root2;
        if(root2==NULL) return root1;
        root1->val+=root2->val;
        root1->left = mergeTrees(root1->left,root2->left);
        root1->right = mergeTrees(root1->right,root2->right);
        return root1;
    }

你可能感兴趣的:(算法,数据结构,c++,深度优先,广度优先,leetcode)