diff --git a/README.md b/README.md index 06802c9e..42de7e13 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ * [二叉树:一入递归深似海,从此offer是路人](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA) * [二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg) * [二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg) + * [二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog) (持续更新中....) @@ -272,6 +273,8 @@ |[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**| |[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**| |[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法**| +|[0501.二叉搜索树中的众数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0501.二叉搜索树中的众数.md) |二叉树 |简单|**递归/中序遍历**| +|[0515.在每个树行中找最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0515.在每个树行中找最大值.md) |二叉树 |简单|**广度优先搜索/队列**| |[0538.把二叉搜索树转换为累加树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0538.把二叉搜索树转换为累加树.md) |二叉树 |简单|**递归** **迭代**| |[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**| |[0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md) |哈希表 |简单|**哈希**| diff --git a/pics/113.路径总和II.png b/pics/113.路径总和II.png new file mode 100644 index 00000000..931c2a9a Binary files /dev/null and b/pics/113.路径总和II.png differ diff --git a/pics/226.翻转二叉树.png b/pics/226.翻转二叉树.png new file mode 100644 index 00000000..1e3aec91 Binary files /dev/null and b/pics/226.翻转二叉树.png differ diff --git a/pics/226.翻转二叉树1.png b/pics/226.翻转二叉树1.png new file mode 100644 index 00000000..9435a12d Binary files /dev/null and b/pics/226.翻转二叉树1.png differ diff --git a/pics/515.在每个树行中找最大值.png b/pics/515.在每个树行中找最大值.png new file mode 100644 index 00000000..e8f2f431 Binary files /dev/null and b/pics/515.在每个树行中找最大值.png differ diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 318f444f..416154ae 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -3,7 +3,7 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ > 我要打十个! -看完这篇文章虽然不能打十个,但是可以迅速打五个!而且够快! +看完这篇文章虽然不能打十个,但是可以迅速打六个!而且够快! # 102.二叉树的层序遍历 @@ -35,7 +35,7 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ 这样就实现了层序从左到右遍历二叉树。 -代码如下:**这份代码也可以作为二叉树层序遍历的模板,以后在打四个就靠它了**。 +代码如下:**这份代码也可以作为二叉树层序遍历的模板,以后再打五个就靠它了**。 ## C++代码 @@ -64,7 +64,7 @@ public: }; ``` -**此时我们就掌握了二叉树的层序遍历了,那么如下四道leetcode上的题目,只需要修改模板的一两行代码(不能再多了),便可打倒!** +**此时我们就掌握了二叉树的层序遍历了,那么如下五道leetcode上的题目,只需要修改模板的一两行代码(不能再多了),便可打倒!** # 107.二叉树的层次遍历 II @@ -226,6 +226,37 @@ public: }; ``` +# 515.在每个树行中找最大值 + +您需要在二叉树的每一行中找到最大的值。 + + + +## 思路 + +层序遍历,取每一层的最大值 + +## C++代码 + +``` + queue que; + if (root != NULL) que.push(root); + vector> result; + while (!que.empty()) { + int size = que.size(); + vector vec; + // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的 + 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; +``` # 总结 二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现(此时是不是又发现队列的应用了)。 @@ -237,8 +268,9 @@ public: * 199.二叉树的右视图 * 637.二叉树的层平均值 * 589.N叉树的前序遍历 +* 515.在每个树行中找最大值 -虽然不能一口气打十个,打五个也还行。 +虽然不能一口气打十个,打六个也还行。 如果非要打十个,还得找叶师傅! diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index cfbc5c9b..b616938f 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -1,5 +1,7 @@ ## 题目地址 +# 112. 路径总和 + ## 思路 相信大家看到千篇一律的写法: diff --git a/problems/0113.路径总和II.md b/problems/0113.路径总和II.md new file mode 100644 index 00000000..7136e9df --- /dev/null +++ b/problems/0113.路径总和II.md @@ -0,0 +1,72 @@ + +## 链接 + +## 思路 + +这道题目与其说是递归,不如说是回溯问题,题目要找到所有的路径。 + +这道题目相对于[112. 路径总和](https://leetcode-cn.com/problems/path-sum/) ,是要求出所有的路径和。 + + +**相信很多同学都疑惑递归的过程中究竟什么时候需要返回值,什么时候不需要返回值?** + +我在[112. 路径总和题解](https://leetcode-cn.com/problems/path-sum/solution/112-lu-jing-zong-he-di-gui-hui-su-die-dai-xiang-ji/)中给出了详细的解释。 + +**如果需要搜索整颗二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。** + +而本题要遍历整个树,找到所有路径,**所以本题的递归函数不要返回值!** + +如图: + + + + +这道题目其实比[112. 路径总和](https://leetcode-cn.com/problems/path-sum/)简单一些,大家做完了本题,可以在做[112. 路径总和](https://leetcode-cn.com/problems/path-sum/)。 + +为了尽可能的把回溯过程体现出来,我写出如下代码(**这个代码一定不是最简洁的,但是比较清晰的,过于简洁的代码不方便读者理解**) + + +## C++代码 + +``` +class Solution { +private: + vector> result; + vector path; + // 递归函数不需要返回值,因为我们要遍历整个树 + void traversal(TreeNode* cur, int count) { + if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点切找到了和为sum的路径 + result.push_back(path); + return; + } + + if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回 + + if (cur->left) { // 左 (空节点不遍历) + path.push_back(cur->left->val); + count -= cur->left->val; + traversal(cur->left, count); // 递归 + count += cur->left->val; // 回溯 + path.pop_back(); // 回溯 + } + if (cur->right) { // 右 (空节点不遍历) + path.push_back(cur->right->val); + count -= cur->right->val; + traversal(cur->right, count); // 递归 + count += cur->right->val; // 回溯 + path.pop_back(); // 回溯 + } + return ; + } + +public: + vector> pathSum(TreeNode* root, int sum) { + result.clear(); + path.clear(); + if (root == NULL) return result; + path.push_back(root->val); // 把根节点放进路径 + traversal(root, sum - root->val); + return result; + } +}; +``` diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index 9c2f5208..04780c6f 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -1,24 +1,59 @@ ## 题目地址 https://leetcode-cn.com/problems/invert-binary-tree/ -## 思路 +# 226.翻转二叉树 -### 递归法 -写递归算法的时候,要想一想是采用前中后序哪种遍历方式 -o -如果对递归还不熟,可以看这里[彻底吃透二叉树的前中后序递归法和迭代法!!](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/che-di-chi-tou-er-cha-shu-de-qian-zhong-hou-xu-d-2/) +翻转一棵二叉树。 -我们先看看递归算法,对于二叉树的递归方式有三种前中后序,先来看看前序遍历。 + -通过动画来看一下翻转的过程: +这道题目背后有一个让程序员心酸的故事,听说 Homebrew的作者Max Howell,就是因为没在白板上写出翻转二叉树,最后被Google拒绝了。(真假不做判断,权当一个乐子哈) - +# 题外话 -递归三部曲: +这道题目是非常经典的题目,也是比较简单的题目(至少一看就会)。 + +但正是因为这道题太简单,一看就会,一些同学都没有抓住起本质,稀里糊涂的就把这道题目过了。 + +如果做过这道题的同学也建议认真看完,相信一定有所收获! + +# 思路 + +我们之前介绍的都是各种方式遍历二叉树,这次要翻转了,感觉还是有点懵逼。 + +这得怎么翻转呢? + +如果要从整个树来看,翻转还真的挺复杂,整个树以中间分割线进行翻转,如图: + + + +可以发现想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。 + +关键在于遍历顺序,前中后序应该选哪一种遍历顺序? (一些同学这道题都过了,但是不知道自己用的是什么顺序) + +遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。 + +**注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果** + +**这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不行,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了** + +那么层序遍历可以不可以呢?**依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!** + +## 递归法 + +对于二叉树的递归法的前中后序遍历,已经在[二叉树:前中后序递归遍历](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA)详细讲解了。 + +我们下文以前序遍历为例,通过动画来看一下翻转的过程: + + + +我们来看一下递归三部曲: 1. 确定递归函数的参数和返回值 -参数就是要传入节点的指针,不需要其他参数了,返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就返回一个指针。 +参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。 + +返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为`TreeNode*`。 ``` TreeNode* invertTree(TreeNode* root) @@ -34,7 +69,7 @@ if (root == NULL) return root; 3. 确定单层递归的逻辑 -因为是先序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。 +因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。 ``` swap(root->left, root->right); @@ -49,34 +84,21 @@ class Solution { public: TreeNode* invertTree(TreeNode* root) { if (root == NULL) return root; - swap(root->left, root->right); - invertTree(root->left); - invertTree(root->right); + swap(root->left, root->right); // 中 + invertTree(root->left); // 左 + invertTree(root->right); // 右 return root; } }; ``` -### 迭代法 +## 迭代法 -[leetcode-master](https://github.com/youngyangyang04/leetcode-master) 中给出了 前中后序迭代法统一的模板,使用前序遍历,只需要改动一行就可以了,代码在下面已经给出。 +### 深度优先遍历 -## C++代码 +[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)中给出了前中后序迭代方式的写法,所以本地可以很轻松的切出如下迭代法的代码: -### 递归(前序遍历) -``` -class Solution { -public: - TreeNode* invertTree(TreeNode* root) { - if (root == NULL) return root; - swap(root->left, root->right); - invertTree(root->left); - invertTree(root->right); - return root; - } -}; -``` -### 迭代法(前序遍历) +C++代码迭代法(前序遍历) ``` class Solution { @@ -86,20 +108,22 @@ public: stack st; st.push(root); while(!st.empty()) { - TreeNode* node = st.top(); + TreeNode* node = st.top(); // 中 st.pop(); - swap(node->left, node->right); - if(node->left) st.push(node->left); - if(node->right) st.push(node->right); + swap(node->left, node->right); + if(node->right) st.push(node->right); // 右 + if(node->left) st.push(node->left); // 左 } return root; } }; ``` +如果这个代码看不懂的话可以在回顾一下[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)。 -### 迭代法(前序遍历)(模板) -模板地址:[leetcode-master](https://github.com/youngyangyang04/leetcode-master) +我们在[二叉树:前中后序迭代方式的统一写法](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)中介绍了统一的写法,所以,本题也只需将文中的代码少做修改便可。 + +C++代码如下迭代法(前序遍历) ``` class Solution { @@ -111,9 +135,9 @@ public: TreeNode* node = st.top(); if (node != NULL) { st.pop(); - if (node->right) st.push(node->right); // 添加右节点 - if (node->left) st.push(node->left); // 添加左节点 - st.push(node); // 添加中节点 + if (node->right) st.push(node->right); // 右 + if (node->left) st.push(node->left); // 左 + st.push(node); // 中 st.push(NULL); } else { st.pop(); @@ -126,4 +150,45 @@ public: } }; ``` + +如果上面这个代码看不懂,回顾一下文章[二叉树:前中后序迭代方式的统一写法](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)。 + +### 广度优先遍历 + +也就是层序遍历,层数遍历也是可以翻转这棵树的,因为层序遍历也可以把每个节点的左右孩子都翻转一遍,代码如下: + +``` +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + queue que; + if (root != NULL) que.push(root); + while (!que.empty()) { + int size = que.size(); + for (int i = 0; i < size; i++) { + TreeNode* node = que.front(); + que.pop(); + swap(node->left, node->right); // 节点处理 + if (node->left) que.push(node->left); + if (node->right) que.push(node->right); + } + } + return root; + } +}; +``` +如果对以上代码不理解,或者不清楚二叉树的层序遍历,可以看这篇[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog) + +# 总结 + +针对二叉树的问题,解题之前一定要想清楚究竟是前中后序遍历,还是层序遍历。 + +**二叉树解题的大忌就是自己稀里糊涂的过了(因为这道题相对简单),但是也不知道自己是怎么遍历的。** + +这也是造成了二叉树的题目“一看就会,一写就废”的原因。 + +**针对翻转二叉树,我给出了一种递归,三种迭代(两种模拟深度优先遍历,一种层序遍历)的写法,都是之前我们讲过的写法,融汇贯通一下而已。** + +大家一定也有自己的解法,但一定要成方法论,这样才能通用,才能举一反三! + > 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 diff --git a/problems/0515.在每个树行中找最大值.md b/problems/0515.在每个树行中找最大值.md new file mode 100644 index 00000000..e1c4863a --- /dev/null +++ b/problems/0515.在每个树行中找最大值.md @@ -0,0 +1,6 @@ + +## 题目地址 +https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/ +## 思路 +详见: + * [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) diff --git a/problems/二叉树的理论基础.md b/problems/二叉树的理论基础.md index e3e1bfc1..a305f05b 100644 --- a/problems/二叉树的理论基础.md +++ b/problems/二叉树的理论基础.md @@ -1,35 +1,176 @@ +# 二叉树理论基础 -二叉树的基础概念 +我们要开启新的征程了,大家跟上! -结点的度:一个结点拥有子树的数目称为结点的度 +说道二叉树,大家对于二叉树其实都很熟悉了,本文呢我也不想教科书式的把二叉树的基础内容在啰嗦一遍,所以一下我讲的都是一些比较重点的内容。 -满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树 +相信只要耐心看完,都会有所收获。 -满二叉树:深度为k,有2^k-1个节点的二叉树 +# 二叉树的种类 + +在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。 + +## 满二叉树 + +满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。 + +如图所示: -如图: -因为在面试中,面试官会说明给出的二叉树,是一科什么样的二叉树,所以大家有必要了解 最基本的二叉树的类型,这样才能和面试官有效沟通。 如果面试官说给出一个完全二叉树,此时候选人不太清楚完全二叉树是什么样的树的话,就会比较麻烦了,如果这种最基础的概念没有弄清,会让面试官对候选人的印象大打折扣。 - -所以基本的概念我们要清楚,这样才能在沟通的过程中 表现出扎实的基本功。 对于二叉树,相信左子树,右子树,父节点,叶子节点,二叉树深度等等这些大家都知道了,我这里单独介绍一下(click) 节点的度:一个结点拥有子树的数目称为结点的度。这个概念在我们介绍二叉树的时候经常用到,一些同学在面试中 描述叶子节点,会说 没有左右孩子的节点,这就显得不够专业, 叶子节点 我们称之为 度为0的节点。 - -那么我们再来介绍几种常见的二叉树类型,(click)什么是满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树, (click)也可以说深度为k,有2^k-1个节点的二叉树 -(click ) 也就是如图所示的树称之为满二叉树。 +这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。 -什么是完全二叉树? +## 完全二叉树 -叶结点只能出现在最底层的两层,且最底层叶结点均处于次底层叶结点的左侧 +什么是完全二叉树? + +完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h -1  个节点。 + +**大家要自己看完全二叉树的定义,很多同学对完全二叉树其实不是真正的懂了。** + +我来举一个典型的例子如题: + + + +相信不少同学最后一个二叉树是不是完全二叉树都中招了。 + +**之前我们刚刚讲过优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。** + +## 二叉搜索树 + +前面介绍的书,都没有数值的,而二叉搜索树是有数值的了,**二叉搜索树是一个有序树**。 +* 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; +* 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; +* 它的左、右子树也分别为二叉排序树 -那么什么是完全二叉树呢, 很多同学经常把 完全二叉树 和 满二叉树 混在一起。 +下面这两棵树都是搜索树 + + + +## 平衡二叉搜索树 + +平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 + +如图: + + + +最后一棵 不是平衡二叉树,因为它的左右两个子树的高度差的绝对值超过了1。 + +**C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树**,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_map底层实现是哈希表。 + +**所以大家使用自己熟悉的编程语言写算法,一定要知道常用的容器底层都是如何实现的,最基本的就是map、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) {} +}; +``` + +大家会发现二叉树的定义 和链表是差不多的,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子. + +这里要提醒大家要注意二叉树节点定义的书写方式。 + +**在现场面试的时候 面试官可能要求手写代码,所以数据结构的定义以及简单逻辑的代码一定要锻炼白纸写出来。** + +因为我们在刷leetcode的时候,节点的定义默认都定义好了,真到面试的时候,需要自己写节点定义的时候,有时候会一脸懵逼! + +# 总结 + +二叉树是一种基础数据结构,在算法面试中都是常客,也是众多数据结构的基石。 + +本篇我们介绍了二叉树的种类、存储方式、遍历方式以及定义,比较全面的介绍了二叉树各个方面的重点,帮助大家扫一遍基础。 + +**说道二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。** + +**那么请跟住Carl的节奏,不仅彻底掌握二叉树的递归遍历,还有迭代遍历!** -(click)来看一下什么是 完全二叉树,叶结点只能出现在最底层的两层,且最底层叶结点均处于次底层叶结点的左侧。也就是最底层的叶子节点 要从左到右来,中间不能断 ,这么说有点抽象,我们来看图 -例如 -(click) 所以是完全二叉树(click) -(click) 所以也是完全二叉树(click) -(click) 所以不是完全二叉树(click) -那么满二叉树是不是完全二叉树呢,答案是:这个当然是了 diff --git a/video/翻转二叉树.gif b/video/翻转二叉树.gif new file mode 100644 index 00000000..afdfe7c0 Binary files /dev/null and b/video/翻转二叉树.gif differ