1 基础知识
1. 二叉树的分类
(1)满二叉树(全部平衡填满):
k层深,2^k-1个节点
(2)完全二叉树(每层填满):
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
堆是一个完全二叉树
(3)二叉搜索树(层次压缩顺序排列):
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
(4)平衡二叉树(左右平衡):
一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。(map、set、multimap,multiset)
2. 遍历方式
(1)深度优先搜索:前序、中序、后序(递归)
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
(2)广度优先搜索:层序(队列迭代)
3. 二叉树的定义
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x): val(x),left(NULL),right(NULL){}
};
2 三种遍历方式
1. 确定递归函数的参数和返回值
2. 确定终止条件
3. 确定单层递归的逻辑
前序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
3 迭代法解三种递归方式
(1)前序遍历
因为是前中后的顺序,因此访问节点和处理节点是一致的。先访问的中间节点也需要先进行处理。因此,先导入中间节点,之后pop出来访问左右。这里先访问右边,因为需要让它存入。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
TreeNode *cur = root;
st.push(cur);
while(!st.empty()){
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;
}
};
注意继续访问的条件是cur->right/left非null,这一点也是单层递归的逻辑。
(2)后序遍历
后序遍历是左右中,反过来就是中右左,将前面的代码中单层递归左右调换一下,最后结果逆转即可。
(3)中序遍历
中序遍历需要先存储左侧的,之后输出中间的,因此访问的要先存起来。方法就是不断向左下延伸,存储,达到尽头输出,之后向右进一步,再向左下延伸。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
TreeNode *cur = root;
while(cur!=NULL || !st.empty()){
if(cur!=NULL){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
result.push_back(cur->val);
cur = cur->right;
}
}
return result;
}
};
(4)统一方法