代码随想录刷题记录(11)| 二叉树(二叉树理论基础,二叉树的递归遍历,迭代遍历,统一迭代,层序遍历)

目录

(一)二叉树理论基础

1. 种类

2. 存储方式

3. 遍历方式

4. 二叉树的定义 

(二)二叉树的递归遍历

1. 思路

2. 递归遍历

(1)前序遍历

(2)中序遍历

(3)后序遍历

(三)二叉树的迭代遍历

1. 思路

2. 迭代遍历

 (1)前序

(2)中序

(3)后序

(四)二叉树的统一迭代

(五)二叉树的层序遍历

1. 思路

2. 层序遍历

3. 相关题目

 (1)二叉树的层序遍历Ⅱ

① 题目描述

② 解题过程 

 (2)二叉树的右视图

① 题目描述

② 解题过程 

 (3)二叉树的层的平均值

① 题目描述

② 解题过程 

 (4)N叉树的层序遍历

① 题目描述

② 解题过程 

 (5)在每个树行中找最大值

① 题目描述

② 解题过程 

 (6)填充每个节点的下一个右侧节点指针

① 题目描述

② 解题过程 

 (7)填充每个节点的下一个右侧节点指针Ⅱ

① 题目描述

② 解题过程 

 (8)二叉树的最大深度

① 题目描述

② 解题过程 

 (9)二叉树的最小深度

① 题目描述

② 解题过程 


(一)二叉树理论基础

1. 种类

  1. 满二叉树:只有度为0的结点和度为2的结点,并且度为0的结点在同一层上。
  2. 完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
  3. 二叉搜索树:节点有序。
  4. 平衡二叉搜索树(AVL 树):它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。【C++中 map、set、multimap、multiset 的底层实现都是 AVL 树,所以 map、set 的增删操作时间时间复杂度是 logn】

2. 存储方式

        二叉树可以链式存储,也可以顺序存储,如果用数组存储,那么如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

3. 遍历方式

二叉树主要有两种遍历方式:

  • 深度优先遍历:先往深走,遇到叶子节点再往回走。(常用栈)
    • 前序遍历(递归法,迭代法)
    • 中序遍历(递归法,迭代法)
    • 后序遍历(递归法,迭代法)
  • 广度优先遍历:一层一层的去遍历。(常用队列)
    • 层序遍历

4. 二叉树的定义 

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

(二)二叉树的递归遍历

1. 思路

递归算法的三要素:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

2. 递归遍历

(1)前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

难易程度:简单

标签:栈、树、深度优先搜索、二叉树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
private:
    void DFS(TreeNode* root, vector<int>& result) {
        if(!root) return;
        result.push_back(root->val);
        DFS(root->left, result);
        DFS(root->right, result);
    }

public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        DFS(root, result);
        return result;
    }
};

(2)中序遍历

94. 二叉树的中序遍历 - 力扣(LeetCode)

难易程度:简单

标签:栈、树、深度优先搜索、二叉树

class Solution {
private:
    void DFS(TreeNode* root, vector<int>& result) {
        if(!root) return;
        DFS(root->left, result);
        result.push_back(root->val);
        DFS(root->right, result);
    }

public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        DFS(root, result);
        return result;
    }
};

(3)后序遍历

145. 二叉树的后序遍历 - 力扣(LeetCode)

难易程度:简单

标签:栈、树、深度优先搜索、二叉树

class Solution {
private:
    void DFS(TreeNode* root, vector<int>& result) {
        if(!root) return;
        DFS(root->left, result);
        DFS(root->right, result);
        result.push_back(root->val);
    }

public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        DFS(root, result);
        return result;
    }
};

(三)二叉树的迭代遍历

1. 思路

        递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。那么用栈也可以是实现二叉树的前后中序遍历。

2. 迭代遍历

 (1)前序

        前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> preStack;
        if(!root) return result;
        preStack.push(root);
        while(!preStack.empty()) {
            TreeNode* node = preStack.top();
            preStack.pop();
            result.push_back(node->val);
            if(node->right) preStack.push(node->right);
            if(node->left)  preStack.push(node->left);
        }
        return result;
    }
};

(2)中序

        中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进 result 数组中),这就造成了处理顺序和访问顺序是不一致的。那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> inStack;
        if(!root)   return result;
        TreeNode* cur = root;
        while(cur || !inStack.empty()) {
            if(cur) {
                inStack.push(cur);
                cur = cur->left;
            }
            else {
                cur = inStack.top();
                inStack.pop();
                result.push_back(cur->val);
                cur = cur->right;
            }
        }
        return result;
    }
};

(3)后序

        先序遍历是中左右,后续遍历是左右中,只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> postStack;
        if(!root)   return result;
        postStack.push(root);
        while(!postStack.empty()) {
            TreeNode* node = postStack.top();
            postStack.pop();
            result.push_back(node->val);
            if(node->left)  postStack.push(node->left);
            if(node->right) postStack.push(node->right);
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

(四)二叉树的统一迭代

        将访问的节点放入栈中,把要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

(五)二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

1. 思路

        层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。需要借用一个辅助队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历。

2. 层序遍历

难易程度:中等

标签:树、广度优先搜索、二叉树

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        queue<TreeNode*> que;
        if(root) {
            que.push(root);
        }
        while(!que.empty()) {
            int size = que.size();
            vector<int> temp;
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                temp.push_back(node->val);
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(temp);
        }
        return result;
    }
};

3. 相关题目

 (1)二叉树的层序遍历Ⅱ

107. 二叉树的层序遍历 II - 力扣(LeetCode)

① 题目描述

        给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

② 解题过程 

 难易程度:中等

标签:树、广度优先搜索、二叉树

        层序遍历结果一层一层倒着输出。

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> result;
        queue<TreeNode*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            vector<int> temp;
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                temp.push_back(node->val);
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(temp);
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

 (2)二叉树的右视图

199. 二叉树的右视图 - 力扣(LeetCode)

① 题目描述

        给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

② 解题过程 

 难易程度:中等

标签:树、深度优先搜索、广度优先搜索、二叉树

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> result;
        queue<TreeNode*> que;
        if(root) {
            que.push(root);
        }
        while(!que.empty()) {
            int size = que.size();
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                // 只有当前层最右侧的放到result
                if(i == size - 1) {
                    result.push_back(node->val);
                }
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return result;
    }
};

 (3)二叉树的层的平均值

637. 二叉树的层平均值 - 力扣(LeetCode)

① 题目描述

        给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。

② 解题过程 

 难易程度:简单

标签:树、深度优先搜索、广度优先搜索、二叉树

        (其中不用那个判断,直接在 for 循环结束后 /size 就可以了) 

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> result;
        queue<TreeNode*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            double count = 0.0;
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                count += node->val;
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
                // 这层的最后一个结点,将这层节点的和除以节点数量
                if(i == size - 1)   result.push_back(count / size);
            }
        }
        return result;
    }
};

 (4)N叉树的层序遍历

429. N 叉树的层序遍历 - 力扣(LeetCode)

① 题目描述

        给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

        树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。

② 解题过程 

 难易程度:中等

标签:树、广度优先搜索

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> result;
        queue<Node*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            vector<int> temp;
            for(int i = 0; i < size; i++) {
                Node* node = que.front();
                que.pop();
                temp.push_back(node->val);
                // 把这一层的子节点都入队
                for(int j = 0; j < node->children.size(); j++) {
                    if(node->children[j])   que.push(node->children[j]);
                }
            }
            result.push_back(temp);
        }
        return result;
    }
};

 

 (5)在每个树行中找最大值

515. 在每个树行中找最大值 - 力扣(LeetCode)

① 题目描述

        给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

② 解题过程 

 难易程度:中等

标签:树、深度优先搜索、广度优先搜索、二叉树

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> result;
        queue<TreeNode*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            int temp = INT_MIN;
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                temp = max(temp, node->val);
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(temp);
        }
        return result;
    }
};

 

 (6)填充每个节点的下一个右侧节点指针

116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)

① 题目描述

        给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
    int val;
    Node *left;
    Node *right;
    Node *next;
}

        填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

        初始状态下,所有 next 指针都被设置为 NULL

② 解题过程 

 难易程度:中等

标签:树、深度优先搜索、广度优先搜索、链表、二叉树

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};
*/

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            Node* pre;
            Node* node;
            for(int i = 0; i < size; i++) {
                // 把当前层第一个节点取出来是pre
                if(i == 0) {
                    pre = que.front();
                    que.pop();
                    node = pre;
                }
                else {
                    node = que.front();
                    que.pop();
                    pre->next = node;
                    pre = pre->next;
                }
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            // 本层最后一个节点指向NULL
            pre->next = NULL;
        }
        return root;
    }
};

 

 (7)填充每个节点的下一个右侧节点指针Ⅱ

117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)

① 题目描述

        给定一个二叉树:

struct Node {
    int val;
    Node *left;
    Node *right;
    Node *next;
}

        填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

        初始状态下,所有 next 指针都被设置为 NULL

② 解题过程 

 难易程度:中等

标签:树、深度优先搜索、广度优先搜索、链表、二叉树

        是不是所谓的“完美二叉树”对代码没有影响,还是一样的。

 

 (8)二叉树的最大深度

104. 二叉树的最大深度 - 力扣(LeetCode)

① 题目描述

        给定一个二叉树 root ,返回其最大深度。

        二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

② 解题过程 

 难易程度:简单

标签:树、深度优先搜索、广度优先搜索、二叉树

class Solution {
public:
    int maxDepth(TreeNode* root) {
        int result = 0;
        queue<TreeNode*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result++;
        }
        return result;
    }
};

 

 (9)二叉树的最小深度

111. 二叉树的最小深度 - 力扣(LeetCode)

① 题目描述

        给定一个二叉树,找出其最小深度。

        最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

        说明:叶子节点是指没有子节点的节点。

② 解题过程 

 难易程度:简单

标签:树、深度优先搜索、广度优先搜索、二叉树

class Solution {
public:
    int minDepth(TreeNode* root) {
        int result = 0;
        queue<TreeNode*> que;
        if(root)    que.push(root);
        while(!que.empty()) {
            int size = que.size();
            result++;
            for(int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
                // 左右都为空表示到了最近叶子节点
                if(!node->left && !node->right) return result;
            }
        }
        return result;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值