二叉树的种类
满二叉树
深度为k,有2^k-1个节点的二叉树。
完全二叉树
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
二叉搜索树
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树的遍历
分为两类遍历方式: 深度优先遍历 跟 广度优先遍历。
深度优先遍历又分为
- 前序遍历
- 中序遍历
- 后序遍历
分辨地方法,可以看中间节点的位置
- 中>>左>>右 : 前序遍历
- 左>>中>>右 : 中序遍历
- 左>>右>>中 : 前序遍历
前序遍历
递归法
class Solution {
public:
void traverse(TreeNode* node, vector<int>& vec){
// 若node为空,说明已经到底层,故返回上一层
if(node == nullptr)
return;
vec.push_back(node->val);
traverse(node->left, vec);
traverse(node->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
迭代法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> nodes;
nodes.push(root);
while(!nodes.empty()){
// 提出stack中最上面的node
TreeNode* cur = nodes.top();
nodes.pop();
// 如果节点提出来是空节点的话,跳过此回圈
if(cur == nullptr) continue;
// 将提出来的node的值放进result里,此时他代表得是局部中中间的node
result.push_back(cur->val);
// 将node得子节点放入stack中,先放入右节点再放入左节点(左节点在上,意味者先操作)
nodes.push(cur->right);
nodes.push(cur->left);
}
return result;
}
};
中序遍历
递归法
class Solution {
public:
void traverse(TreeNode* node, vector<int>& vec){
if(node == nullptr)
return;
traverse(node->left, vec);
vec.push_back(node->val);
traverse(node->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
迭代法
class Solution {
public:
// 利用指针来帮助遍历树
// 中序遍历就是 左中右,当指针指向一个节点的时后,
// 要先去看是否有左子节点,如果有则指针指向左子节点
// 如果没有,则像数组除存自己的直(此时他自己就是中间节点)
// 然后往栈里面放入右子节点
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> nodes;
vector<int> res;
TreeNode* cur = root;
while(cur != nullptr || !nodes.empty()){
if(cur != nullptr){
nodes.push(cur);
cur = cur->left;
}
else{
cur = nodes.top();
nodes.pop();
res.push_back(cur->val);
cur = cur->right;
}
}
return res;
}
};
后序遍历
递归法
class Solution {
public:
void traverse(TreeNode* node, vector<int>& vec){
if(node == nullptr)
return;
traverse(node->left, vec);
traverse(node->right, vec);
vec.push_back(node->val);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
迭代法
我们可以利用后序遍历跟前序遍历的顺序排列来进行迭代
前序转成后序如下:
中左右 >> 中右左 >> 左右中
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> nodes;
vector<int> result;
nodes.push(root);
// 先前序遍历,在翻转结果
while(!nodes.empty()){
TreeNode* cur = nodes.top();
nodes.pop();
if(cur == nullptr) continue;
result.push_back(cur->val);
// 前序遍历变一下 -> 中右左
nodes.push(cur->left);
nodes.push(cur->right);
}
// 翻转数组 中右左 -> 左右中
reverse(result.begin(), result.end());
return result;
}
};
广度优先遍历(层次遍历)
迭代法
核心是利用queue先进先出的概念,同时当每一层的节点都拿出来得时后,queue得大小刚好是下一层的大小,我们可以利用回圈把下一层的节点拿出来做操作。如此反复
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> nodes;
vector<vector<int>> result;
if(root) nodes.push(root);
while(!nodes.empty()){
vector<int> vec;
int size = nodes.size();
for(int i = 0; i < size; i++){
TreeNode* cur = nodes.front();
nodes.pop();
vec.push_back(cur->val);
if(cur->left) nodes.push(cur->left);
if(cur->right) nodes.push(cur->right);
}
result.push_back(vec);
}
return result;
}
};
226
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
// 中序便利,且顺序为中>>右>>左
stack<TreeNode*> treeStack;
// 如果root是null,回传null
if(!root) return root;
treeStack.push(root);
while(!treeStack.empty()){
TreeNode* cur = treeStack.top();
treeStack.pop();
swap(cur->left, cur->right);
if(cur->right) treeStack.push(cur->right);
if(cur->left) treeStack.push(cur->left);
}
return root;
}
};