代码随想录算法训练营43期 | Day 13 —— 二叉树part01

代码随想录算法训练营

二叉树理论基础

二叉树的种类

1. 满二叉树

满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。

性质:深度为K ,节点数: 2^k-1

2. 完全二叉数

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。

底层从左到右是连续的。

3. 二叉搜索树

二叉搜索树是有数值的,二叉搜索树是一个有序树。

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

3. 平衡二叉搜索树

又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_set底层实现是哈希表。

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

那么链式存储方式就用指针, 顺序存储的方式就是用数组。
链式存储:

顺序存储:

用数组来存储二叉树如何遍历的呢?

如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

但是用链式表示的二叉树,更有利于我们理解,所以一般我们都是用链式存储二叉树。

二叉树遍历方式

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

  1. 深度优先遍历:先往深走,遇到叶子节点再往回走。(前中后序遍历)------递归、迭代法
  2. 广度优先遍历:一层一层的去遍历。(层序遍历)------迭代法

前序遍历:中左右

中序遍历:左中右

后序遍历:左右中

二叉树的定义

c++ 复制代码
struct TreeNode{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x):val(x),left(NULL),right(NULL) {}
};

二叉树的递归遍历

递归三步走:

  1. 确定递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

144.前序遍历

中 左 右

c++ 复制代码
 //构造递归函数,确定传入的参数,前序遍历,顺序:中左右,传入节点,
    void preorder(TreeNode* root, vector<int>& result)
    {
        if(root==nullptr) return;
        //中
        result.push_back(root->val);
        //左
        preorder(root->left,result);
        //右
        preorder(root->right, result);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        preorder(root, result);
        return result;
    }

145.后序遍历

c++ 复制代码
//递归函数
    void backorder(TreeNode* root, vector<int>& result)
    {
        //1. 定义递归终止条件
        if(root==nullptr) return;
        //左 右  中
        backorder(root->left, result);
        backorder(root->right, result);
        result.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        //数组 ,存储结果
        vector<int> result;
        backorder(root, result);
        return result;
    }

94.中序遍历

c++ 复制代码
 //中序遍历
    void midorder(TreeNode* cur, vector<int>& result)
    {
        //递归结束
        if(cur==nullptr) return;
        //左
        midorder(cur->left,result);
        //中
        result.push_back(cur->val);
        //右
        midorder(cur->right, result);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        midorder(root, result);
        return result;
    }

二叉树迭代遍历

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

用栈实现前中后序遍历

前序遍历(迭代法)

c++ 复制代码
 vector<int> preorderTraversal(TreeNode* root) {
        //迭代法实现前序遍历
        //数组 result存储 遍历结果
        vector<int> result;
        //栈模拟实现递归过程,栈的数据类型是二叉树节点(指针)
        stack<TreeNode*> st;
        if(root==nullptr) return result;
        //root根节点入栈
        st.push(root);
        //中 左 右 
        while(!st.empty())
        {
            //取出栈中元素,将其放入到result数组中
            TreeNode* cur = st.top();
            st.pop();
            result.push_back(cur->val);
            //先右后左,出栈顺序为左、右,符合前序遍历逻辑
            if(cur->right) st.push(cur->right);
            if(cur->left)  st.push(cur->left);
        }
        return result;
    }

后序遍历(迭代法)

c++ 复制代码
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        //前序遍历 中左右 -> 中右左-> 翻转reverse -> 左右中
        vector<int> result;
        stack<TreeNode*> st;
        //root为nullptr 返回result
        if(root==nullptr) return result;
        st.push(root);
        //循环遍历
        while(!st.empty())
        {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            
            if(node->left)  st.push(node->left);
            if(node->right) st.push(node->right);
            
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

中序遍历(迭代法)

c++ 复制代码
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

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

解题思路:

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
流程:

  1. 判断根节点是否为空,否则存入队列中;
  2. 遍历的终止条件,queue中为空,则停止遍历;
  3. 控制每一层的弹出节点的个数,size存当前queue中的元素,当size=1时,则为第一层,循环条件为一次;将queue中节点弹出并存放到vector一维数组中;
  4. 向下移动节点,根节点的左节点和右节点入队;
  5. 得到当前队列的size = 2;循环终止2次;弹出左节点,并存放到一维数组中,并将左节点的左子节点和右子节点存放到队列中;第二次循环,弹出右节点,并将右左子节点和右右子节点插入队列中;
  6. 获得当前队列的长度为size = 4;弹出队列中元素存放到一维数组中;
  7. 最后将一位数组结果保存到二维数组中,返回result;

核心需要记录size,记录的是本层的需要弹出的节点数量

c++ 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        //存储二维数组,最终结果
        vector<vector<int>> result;
        //1 判断root不为空,将第一个节点加入到队列中
        if(root!=nullptr) que.push(root);
        //2 遍历终止条件,队列中没有元素,!que.empty();
        while(!que.empty())
        {
            // 3 控制当前节点数量 size ,控制弹出元素的数量
            int size = que.size();
            // 4 定义一维数组,存放队列中元素,最终结果需要用二维数组显示
            vector<int> vec;
            //5 遍历队列
            for(int i = 0; i<size;i++)
            {
                // 获取队列中的元素,弹出
                TreeNode* node = que.front();
                que.pop();
                // 存储结果,记录完本层结果
                vec.push_back(node->val);
                //节点移到下一层
                if(node->left)  que.push(node->left);
                if(node->right) que.push(node->right);
            }
            //一维数组保存后
            result.push_back(vec);
        }
        return result;
    }
};
相关推荐
浅念同学5 分钟前
算法.图论-并查集上
java·算法·图论
何不遗憾呢14 分钟前
每日刷题(算法)
算法
立志成为coding大牛的菜鸟.18 分钟前
力扣1143-最长公共子序列(Java详细题解)
java·算法·leetcode
鱼跃鹰飞18 分钟前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先
liangbm324 分钟前
数学建模笔记——动态规划
笔记·python·算法·数学建模·动态规划·背包问题·优化问题
潮汐退涨月冷风霜30 分钟前
机器学习之非监督学习(四)K-means 聚类算法
学习·算法·机器学习
B站计算机毕业设计超人36 分钟前
计算机毕业设计Python+Flask微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI
爬虫·python·深度学习·算法·机器学习·自然语言处理·数据可视化
羊小猪~~40 分钟前
深度学习基础案例5--VGG16人脸识别(体验学习的痛苦与乐趣)
人工智能·python·深度学习·学习·算法·机器学习·cnn
Charles Ray2 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码2 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记