终于开了二叉树!!从这开始我就学的不好了!!看我猛猛学!!
先不刷题先从理论看起来
首先二叉树的种类:
- 满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
- 完全二叉树:除了底层以外其他层都是满的,底层从左到右节点连续。(堆就是一个完全二叉树)
- 二叉搜索树:(对节点个数没要求,但是对节点上元素顺序有要求)
- 平衡二叉搜索树:左右子树的深度之差的绝对值不大于1.(往里插入节点是Logn级别的,查询也是Logn级别的)是map\set等数据结构的底层实现(这些数据结构中的key和元素都是有序的)
二叉树的存储方式:
- 链式存储:指针指向左右孩子(一般用这种)
- 线性存储:2i+1左孩子的下标,2I+2右孩子的下标
二叉树遍历:
-
深度优先搜索:前中后都是这种遍历。一般用递归来实现,也可以用栈来模拟递归过程。
其中,
前序遍历:中左右
中序遍历:左中右
后序遍历:左右中 -
广度优先搜索:一层一层遍历。层序遍历就是广度优先搜索。迭代法实现。使用队列。
二叉树不建议长时间在力扣上的核心代码模式来写,因为面试的时候可能会让写一个二叉树结构的代码。
之前写过遍历,也记得是有递归和非递归两种写法,但非递归法每次都不得要领,只是看了答案以后能暂时写出来的程度。。。流汗了
但遍历的两种方法是必须要会的。
看一下题解:
今天写遍历,直接上题目!
144.二叉树的前序遍历
首先用递归方法写一遍,很短很简单
/**
* 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 {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
process(root,result);
return result;
}
void process(TreeNode* root, vector<int>& result){
if(root==nullptr) return;
result.push_back(root->val);
if(root->left !=nullptr) process(root->left, result);
if(root->right !=nullptr) process(root->right, result);
return;
}
};
递归简言之就是把当前头节点的左右孩子不停放入下一个递归直至节点为空。但其实不应该这么描述,递归也应该有一个很严谨的分析思路的。
深度优先搜索是可一个方向往里递归,最终碰到边界再返回。
看了视频的题解be like:
- 明确每次递归的参数和返回值(递归返回值一般就是void)
那么这里我的参数是头节点和一个用来放结果的数组(的取地址&) - 明确每次递归的终止条件(这里就是遇到nullptr就终止)
- 确定单层递归的逻辑(这里就是前序or中后序遍历的输出顺序)
那么前序遍历的非递归方法?
由于递归的底层是用栈来实现的,因此用非递归模拟递归的方式也应该用栈来做。
写一下:
/**
* 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 {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> myStack;
vector<int> result;
if(root==nullptr) return result;
myStack.push(root);
while(myStack.empty()==0){
TreeNode* t= myStack.top();
result.push_back(t->val);
myStack.pop();
if(t->right != nullptr) myStack.push(t->right);
if(t->left !=nullptr) myStack.push(t->left);
}
return result;
}
};
准备一个栈,头节点先入栈。
接下来如果栈不为空就持续循环:
栈顶踢出一个元素存入数组。并将该元素的右孩子,左孩子按顺序入栈(没有就不入)。再跳到下一次循环。
为什么是先右孩子再左孩子?栈是先进后出的,前序遍历想要得到的顺序是中左右,只有左孩子后入栈,下次循环中先出栈的才是左孩子,从而实现左子在右子之前输出。
odk那么鉴于后序遍历的非递归方式和前序遍历的非递归方式非常类似,这里先写一下后序遍历好了.
145. 二叉树的后序遍历
很快AC了不多赘述。递归的思路与前序遍历几乎一样,只是需要改变一下递归函数中的输出顺序(root的val最后入数组,因为root代表的是当前的“中”)
/**
* 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 {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
process(root, result);
return result;
}
void process(TreeNode* root, vector<int>& result){
if(root==nullptr) return;
if(root->left !=nullptr) process(root->left, result);
if(root->right != nullptr) process(root->right, result);
result.push_back(root->val);
}
};
非递归方式遍历:
依然是用栈来实现。后序的输出顺序是“左右中”。用栈实现的话,第一个头节点总是要先入栈,然后再在第一次循环中先出栈的,这样实现不了把中放在最后。
因此可以给出的一个思路就是:
中先出栈,入栈的时候顺序为先左后右。
这样整体的数组中存储的顺序就为:中右左。
最后使用双指针调转数组即可。
代码如下:
/**
* 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 {
public: // 先写一个调转结果的
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
if (root == nullptr)
return result;
stack<TreeNode*> st;
st.push(root);
while (st.empty() == 0) {
TreeNode* t = st.top();
result.push_back(t->val);
st.pop();
if (t->left != 0)
st.push(t->left);
if (t->right != 0)
st.push(t->right);
}
int n=result.size();
int left=0,right=n-1;
while(left<right){
int a=result[left];
result[left]=result[right];
result[right]=a;
++left;
--right;
}
return result;
}
};
94 二叉树的中序遍历
先来递归:
/**
* 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 {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
process(root, result);
return result;
}
void process(TreeNode* root , vector<int>& result){
if(root==nullptr) return;
process(root->left, result);
result.push_back(root->val);
process(root->right, result);
}
};
递归思路就不提了,没什么变化。
再来看非递归型中序遍历:
中序遍历需要:左中右。又回到那个问题上了:头节点作为“中”,先入栈后在第一次循环中就出栈了,顺序没法放在左子的后面。
因此中序遍历的迭代方法无法与前序和后序通用。
上代码:
/**
* 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 {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
if(root==nullptr) return result;
stack<TreeNode*> st;
//st.push(root);
TreeNode* curr=root;
while(st.empty()==0 || curr!=nullptr){
if(curr!=nullptr){
st.push(curr);
curr=curr->left;
}
else{
TreeNode* it=st.top();
st.pop();//这里要出栈
result.push_back(it->val);
curr=it->right;
}
}
return result;
}
};
这个写法里,首先构造一个空栈(先不放入头节点)
构建一个指针用来遍历,它首先指向头节点。
接下来开始循环,为的是先遍历到左子的边界(从最左的子节点开始输出)如果栈不为空或当前指针不为空,都可以进入循环。
当前指针若不为空,说明还没有遍历到最左的边界,就入栈。指针继续向当前的左子移动。
当前指针若为空,说明已到边界,此时从栈顶弹出一个元素,该元素的val存入输出结果中,curr指向该元素的右子。
好了,接下来看统一迭代。
什么是统一迭代?
“
我们发现迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。
实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。
”
看了一下统一迭代的代码感觉有点点麻烦。。。暂时不看了