算法专题 | 树

阅读更多文章,请看学习笔记汇总

树的基本术语

是由若干结点(A,B,C都是结点),是由唯一的根(结点A)和若干棵互不相交的子树(如B、E、F、K、L这五个结点组成的树就是一棵子树)组成的

在这里插入图片描述
结点的度:结点拥有的子树个数或者分支的个数
树的度:树中各结点度的最大值,如图6-1结点度最大为3(A,D结点)
叶子结点:也叫终端结点,指度为0的结点,如K,L,F,G,M,I,J
分支结点:也叫非终端结点,指度不为0的结点,如A,B,C…
内部结点:除了根节点的非终端结点,如BCDEH结点

孩子:结点的子树的根,如A结点的孩子是B、C、D
双亲:与孩子的定义对应,如B、C、D结点的双亲都是A
兄弟:同一双亲的孩子之间互为兄弟,如B、C、D互为兄弟
祖先:从根到某结点的路径上的所有结点,如K的祖先是A、B、E
子孙:以某结点为根的子树中的所有结点,都是该结点的子孙。如D的子孙是H、I、J、M
层次:从根开始,根为第一层,根的孩子为第二层,根的孩子的孩子为第三层,以此类推
树的高度或深度:树中结点的最大层次。如图中树共有4层,所以高度为4

森林:若干棵互不相交的树的集合。如把根节点A去掉,剩下的3棵子树互不相交,它们组成一个森林

二叉搜索树
具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树

完全二叉树
定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边


定义:堆是一个完全二叉树,每个节点与其子节点位置相对。父节点总是大于或等于子节点,这种情况下被叫作大顶堆,或者父节点总是小于或等于子节点,这种情况下叫作小顶堆。注意,给定父节点的子节点不一定按顺序排列

堆(堆像一棵倒过来的树):是一种经过排序的树形数据结构,每个结点都有一个值。通常所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列

树的结点定义

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

一般给的函数中参数是TreeNode* root,相当于告诉你一个指向树根结点的指针,root->val访问树根结点的值,root->left表示一个指向左子树根结点的指针,root->right表示一个指向右子树根结点的指针

树的遍历

递归写法

*递归理解方法

递归可以这样理解:就看一个分叉,把分支当做一个整体,然后在开头添加递归出口即可
出口必须先写,不然会出现段错误

递归是从顶部到底部再回到顶部,动态规划是通过存储,直接从底部到顶部解决问题

递归经典例子

int Fibonacci(int n) {
	if (n == 0) return 0;
	else if (n == 1) return 1;
	else return Fibonacci(n - 1) + FIbonacci(n - 2);
}

以先序遍历为例
如果二叉树为空树,则什么都不做,否则:

  1. 访问根结点
  2. 先序遍历左子树
  3. 先序遍历右子树

对应的算法描述如下:

void preorder(TreeNode *p) {
    if (p) {
        Visit(p);           // Visit()可以是对结点p的任何访问操作
        preorder(p->left);  // 先序遍历左子树
        preorder(p->right); // 先序遍历右子树 
    }
}

中序遍历 inorder
先左子树再根结点点最后右子树

后序遍历 postorder
先左子树再右子树最后根结点

经典迭代写法

中序遍历 迭代

在这里插入图片描述
将整棵树的最左边一条链压入栈中
每次取出栈顶元素,如果它有右子树,则将右子树压入栈中

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

// 中序遍历,迭代写法
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        auto cur = root;
        while(cur || stk.size()) {
            while(cur) { // 结点非空时,把左侧的点全加到栈,然后倒着遍历
                stk.push(cur);
                cur = cur->left;
            }
            // stk:1 2 4 8
            cur = stk.top();
            stk.pop(); 
            res.push_back(cur->val);
            cur = cur->right; // 遍历该结点的右子树,如果为空,则继续栈
        }
        return res;
    }
};

先序遍历 迭代

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        auto cur = root;
        while(cur || stk.size()) {
            while(cur) { // 把左侧的点全加到栈,然后倒着遍历
                res.push_back(cur->val); // 先访问根结点
                stk.push(cur);
                cur = cur->left; 
               
            }
            // stk:1 2 4 8
            cur = stk.top();
            stk.pop(); 
            cur = cur->right; // 类似地遍历右子树
        }
        return res;
    }
};

后序遍历 迭代 lc145???

层次遍历(队列)

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

 // 2020年1月16日 星期四 16:26:37
 // 用队列,每次记下当前层有len个结点,然后遍历这len个点
class Solution {
public:
    static const int N =  1e4;
    TreeNode* q[N];
    int hh = 0, tt = 0;
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if (!root) return {};
        q[tt ++] = root;
        while(hh < tt) {
            int len = tt - hh; // 当前层的结点个数
            vector <int> tmp;
            for(int i = 0; i < len; i ++) {
                auto t = q[hh];
                tmp.push_back(q[hh]->val);
                hh ++;
                if (t->left) q[tt ++] = t->left;
                if (t->right) q[tt ++] = t->right;  
            }
            res.push_back(tmp);
        }
        return res;
    }
};

输出二叉搜索树

中序遍历即可

 	vector<int> ans;
    void dfs(TreeNode* p) {
        if (!p) return;
        dfs(p->left);
        ans.push_back(p->val);
        dfs(p->right);
    } 

得到的ans是有序的

树的例题

102.树的最大深度(递归或层次遍历)

在这里插入图片描述
递归

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

int dfs(TreeNode* root) {
    if (!root) return 0;
    return max(dfs(root->left), dfs(root->right)) + 1;
}

class Solution {
public:
    int maxDepth(TreeNode* root) {
        return dfs(root);
    }
};

层次遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (!root) return 0;
        queue <TreeNode*> q;
        q.push(root);

        // 层次遍历
        int level = 0;
        while(q.size()) {
            int len = q.size();
            int flag = 0;
            for (int i = 1; i <= len; i ++) {
                auto t = q.front();
                q.pop();
                if (t->left) q.push(t->left);
                if (t->right) q.push(t->right);
            }
            level ++;
        }

        return level;
    }
};

111.树的最小深度(层次遍历)

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 // 2020年1月16日 星期四 18:54:16
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (!root) return 0;
        queue <TreeNode*> q;
        q.push(root);

        // 层次遍历,记录level,如果当前层出现叶子结点,则退出while
        int level = 0;
        while(q.size()) {
            int len = q.size();
            int flag = 0;
            for (int i = 1; i <= len; i ++) {
                auto t = q.front();
                q.pop();
                if (t->left) q.push(t->left);
                if (t->right) q.push(t->right);
                if (t->left == NULL && t->right == NULL) flag = 1;
            }
            level ++;
            if (flag == 1) {
                break;
            }
        }

        return level;
    }
};

662.二叉树的最大宽度(层次遍历+编号)

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

// 层次遍历,然后对每个结点编号(结构体),每一层记录最小的编号和最大的编号,算出深度
// 此题数据范围比较大,32位整数,故要用double来存
class Solution {
public:
    // 构造指针+编号的结构体
    typedef struct node {
        TreeNode *a;
        double b;
    } node;

    int widthOfBinaryTree(TreeNode *root) {
        queue <node> q;
        q.push({root, 1});
        double res = 1;
  
        while(q.size()) {
            int len = q.size();
            double mn = DBL_MAX, mx = 0; // DBL_MAX是double的最大值 大约10^308次

            // 对于每一层找到左边界和右边界
            for (int i = 1; i <= len; i ++) {
                auto t = q.front();
                q.pop();
                if (t.a->left) {
                    q.push({t.a->left, 2 * t.b});
                    mn = min(mn, 2 * t.b);
                    mx = max(mx, 2 * t.b);
                }
                if (t.a->right) {
                    q.push({t.a->right, 2 * t.b + 1});
                    mn = min(mn, 2 * t.b + 1);
                    mx = max(mx, 2 * t.b + 1);
                }
            }

            res = max(res, (double)((int)(mx - mn) + 1));
        }

        return (int)res;
    }
};

98.验证二叉搜索树

在这里插入图片描述

法一:递归判断每个点是否在范围内

// 2019年12月23日 星期一 12:40:08
// 第一个点可以取的范围在INT_MIN 到 INT_MAX
// 然后递归判断每个点是否在范围内

// v可能是INT_MIN,再-1会爆int,所以用long long和1ll
bool dfs(TreeNode* root, long long Left, long long Right) { 
    if (!root) return true;
    int v = root->val;
    if (v < Left || v > Right) return false;
    
    return dfs(root->left, Left, v - 1ll) && dfs(root->right, v + 1ll, Right);
}

class Solution {
public:    
    bool isValidBST(TreeNode* root) {
        return dfs(root, INT_MIN, INT_MAX);
    }
};

法二:中序遍历判断是否递增

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
vector<int> vec;

// 中序遍历
void inorder(TreeNode* p) {
    if (p) {
        inorder(p->left);
        vec.push_back(p->val);
        inorder(p->right);
    }
}

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        vec.clear(); // 清空vector
        inorder(root);
        for (int i = 1;i < vec.size(); i ++) {
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

94.二叉树的中序遍历(迭代写法)

在这里插入图片描述

/**
 *    @Author: Wilson79
 *    @Datetime: 2019年12月23日 星期一 13:34:05
 *    @Filename: 中序遍历迭代写法.cpp
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

// 中序遍历,迭代写法
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        auto cur = root;
        while(cur || stk.size()) {
            while(cur) { // 结点非空时,把左侧的点全加到栈,然后倒着遍历
                stk.push(cur);
                cur = cur->left;
            }
            // stk:1 2 4 8
            cur = stk.top();
            stk.pop(); 
            res.push_back(cur->val);
            cur = cur->right; // 遍历该结点的右子树,如果为空,则继续栈
        }
        return res;
    }
};

100.相同的树

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (!p || !q) return (p == q);
        if (p->val != q->val) return false; // 如果当前点相同,则看他们的子树的情况
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); 
    }
};

104.二叉树的最大高度

递归经典例子
在这里插入图片描述

// 2019年12月24日 星期二 13:31:54
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

int dfs(TreeNode* root) {
    if (!root) return 0;
    return max(dfs(root->left), dfs(root->right)) + 1;
}

class Solution {
public:
    int maxDepth(TreeNode* root) {
        return dfs(root);
    }
};

105.从前序与中序遍历序列构造二叉树(递归)

在这里插入图片描述

/**
 *    @Author: Wilson79
 *    @Datetime: 2019年12月24日 星期二 13:34:41
 *    @Filename: 105.从前序与中序遍历序列构造二叉树.cpp
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

// 前序遍历 preorder = [“3”,9,20,15,7]
// 中序遍历 inorder =  [9,“3”,15,20,7]
// "3"根节点,分成了两部分

class Solution {
public:
    unordered_map <int, int> hash;

    TreeNode *dfs(vector<int> &preorder, vector<int> &inorder, int pre1, int pre2, int in1, int in2) {
        if (pre1 > pre2) return NULL; // 出口必须先写
        int val = preorder[pre1]; // 拿出根节点的val
        auto root = new TreeNode(val); // 创建一个val,NULL,NULL的结点
        

        int k = hash[val];
        int len = k - in1; // 表示先序遍历的长度
        root->left = dfs(preorder, inorder, pre1 + 1, pre1 + 1 + len - 1, in1, k - 1);
        root->right = dfs(preorder, inorder, pre1 + 1 + len, pre2, k + 1, in2);
        return root;
    }

    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
        int n  = preorder.size();
        for (int i = 0; i < inorder.size(); i++) hash[inorder[i]] = i;
        return dfs(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};

106.从中序与后序遍历序列构造二叉树

/**
 *    @Author: Wilson79
 *    @Datetime: 2019年12月24日 星期二 18:29:11
 *    @Filename: 106.从中序与后序遍历序列构造二叉树.cpp
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

// 后序遍历 postorder = [9,15,7,20,"3"]
// 中序遍历 inorder   = [9,"3",15,20,7]
//     3
//   9  20
//     15 7

// 跟105的做法是相同的

class Solution {
public:
    unordered_map <int, int> hash;

    TreeNode* dfs(vector<int>& inorder, vector<int>& postorder, int p1, int p2, int in1, int in2) {
        // p1后续遍历左端点,p2后续遍历右端点,in1中序遍历左端点,in2中序遍历右端点
        if (p1 > p2) return NULL;

        int val = postorder[p2];
        int key = hash[val];
        int len = key - in1; // 左子树长度
        auto root = new TreeNode(val);
        root->left = dfs(inorder, postorder, p1, p1 + len - 1, in1, key - 1);
        root->right = dfs(inorder, postorder, p1 + len, p2 - 1, key + 1, in2);
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
        return dfs(inorder, postorder, 0, n - 1, 0, n - 1);
    }
};



110.平衡二叉树

在这里插入图片描述

法一:O(n^2)

/**
 *    @Author: Wilson79
 *    @Datetime: 2019年12月24日 星期二 19:26:00
 *    @Filename: 110.平衡二叉树.cpp
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
// 时间复杂度O(n^2)

class Solution {
public:
    // 求子树的高度
    int height(TreeNode *p) {
        if (!p) return 0;
        return max(height(p->left), height(p->right)) + 1;
    }

    bool isBalanced(TreeNode *root) {
        if (!root) return true; // 如果它是空的,这个子树自然满足平衡二叉树
        if (abs(height(root->left) - height(root->right)) > 1) return false;

        return isBalanced(root->left) && isBalanced(root->right);
    }
};

法二:O(n) 递归高度时直接判断

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    // 朴素算法中很多高度已经算过了,所以在算高度的时候直接可以判断
    int height(TreeNode* p) {
        if (!p) return 0;
        int lh = height(p->left); 
        if (lh < 0) return -1; // 如果这棵子树已经是非平衡二叉树,就要一直返回到顶部
        // 当发现lh返回-1的时候,我们需要提前返回-1
        int rh = height(p->right);
        if (rh < 0) return -1;
        
        if (abs(lh - rh) > 1) return -1;
        return max(lh, rh) + 1;
    }
    
    bool isBalanced(TreeNode* root) {   
        return height(root) != -1;
    }
};

两个二叉搜索树中所有元素

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> ans;
    void dfs(TreeNode* p) {
        if (!p) return;
        dfs(p->left);
        ans.push_back(p->val);
        dfs(p->right);
    } 
    
    vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
        dfs(root1);
        dfs(root2);
        sort(ans.begin(), ans.end());
        return ans;
    }
};
发布了182 篇原创文章 · 获赞 71 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/103662932