参考链接:代码随想录
深度为k,节点数为2^k-1,其中k从1开始
除了最底层节点未满,其他层节点都满,且最底层节点集中到左边。**即从上往下从左往右遍历能连着。**最底层为k,则最底层节点数从1到2^(k-1)
树节点有元素,且对每个子树,左侧所有节点都小于根节点,右侧都大于根节点。
数组存储和链式存储。
DFS:前序,中序,后序。
前序:中左右;中序:左中右;后序:左右中。
BFS:从上往下从左往右。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
两个左右子树高度相差不过1。常见的就是红黑树。
144.二叉树的前序遍历
145.二叉树的后序遍历
94.二叉树的中序遍历
思路:递归写法,首先要确定几个条件,首先是结束条件和返回值,然后是递归逻辑。对于二叉树的递归遍历,相对比较简单,即节点为空则停止遍历,否则对左右子树分别递归。时间复杂度O(n)。
前序代码:
class Solution {
public:
void preTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
if(!root){
return;
}
ans.push_back(root->val);
preTra(ans,root->left);
preTra(ans,root->right);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
preTra(ans,root);
return ans;
}
};
后序代码:
class Solution {
public:
void postTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
if(!root){
return;
}
postTra(ans,root->left);
postTra(ans,root->right);
ans.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
postTra(ans,root);
return ans;
}
};
中序代码:
class Solution {
public:
void inTra(vector<int> &ans,TreeNode* root){//注意递归要返回void,需要单独写个函数,否则无法写入ans中
if(!root){
return;
}
inTra(ans,root->left);
ans.push_back(root->val);
inTra(ans,root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
inTra(ans,root);
return ans;
}
};
思路:递归可以想到使用栈来解决,我们先看前序,顺序是根->左->右,碰到root节点,首先就将其入栈,然后出栈代表遍历这个节点,对两个子节点,需要先将右节点进栈,然后再将左节点进栈。这样出栈的时候,就是先出左,再出右。根节点一开始就处理了,处理完根节点后立刻将右左进栈处理。注意根节点一开始最好入栈,这样才能根据栈不为空进行while循环。
一开始写的错误代码:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
stack<int> st;
if(!root){
return ans;
}
st.push(root->val);
while(!st.empty()){
ans.push_back(st.top());//先头节点
if(root->right)
}
}
};
主要原因是我将节点中的val入栈,导致再处理完根节点后不知如何处理左右节点,因为仅仅根据st头部的一个值无法找到左右节点,循环也无法进行,看完解析后发现需要将整个节点存入栈中。
标答:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
if(!root){
return ans;
}
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
ans.push_back(node->val);//先头节点
if(node->right){
st.push(node->right);
}
if(node->left){
st.push(node->left);
}
}
return ans;
}
};
后序为左->右->中,解决方法类似,注意我们通过栈的处理,一定是先处理的根节点,将后序反过来,就是中->右->左,只需要将前序的左右顺序一调换,然后最后将结果做一个翻转就OK。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
if(!root){
return ans;
}
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
ans.push_back(node->val);//先头节点
if(node->left){
st.push(node->left);
}
if(node->right){
st.push(node->right);
}
}
reverse(ans.begin(),ans.end());
return ans;
}
};
最麻烦的是中序遍历,由于是左->根->右的顺序,不管是否倒转,都无法先处理根节点。所以代码会复杂一些,思路主要是使用指针用来遍历所有节点,然后栈用来存放节点。对所有子树,进栈顺序为中,左,右,注意当中节点出栈后,才能处理右节点。
一开始的错误代码:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
if(!root){
return ans;
}
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();//遍历节点
if(node->left){//左节点不为空,一直往左遍历,入栈
node=node->left;
st.push(node);
}
else{//左节点为空,遍历中节点,出栈
ans.push_back(node->val);
st.pop();
if(node->right){//右节点不为空
node=node->right;
st.push(node);
}
}
}
return ans;
}
};
主要问题是在左节点不为空的时候,遍历完左节点后,下次访问栈顶的根节点,还是会又根据左节点不为空继续访问左节点。看完标答后,发现需要直接将cur指针完全用于遍历过程,然后在一开始就判断目前访问的节点为不为空。
标答:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
TreeNode* node=root;//遍历节点
while(node||!st.empty()){//node不为空说明目前遍历的节点需要入栈,栈不为空说明还有节点没遍历完
if(node){//node不为空,入栈,一直往左遍历
st.push(node);
node=node->left;
}
else{//处理栈中元素
node=st.top();
ans.push_back(node->val);//记住入栈一定是目前访问的节点,也就是中节点
st.pop();
node=node->right;//这里不用考虑右为不为空,如果为空下次循环再处理
}
}
return ans;
}
};
总结:前序遍历中访问节点和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!
pass