刷题记录Day14-二叉树
文章目录
前言
题目来源:leetcode
刷题顺序:代码随想录
刷题工具:VSCode+leetcode插件
补充:延毕时间充裕,会结合LeetCode 101: A LeetCode Grinding Guide (C++ Version)相似题目一起做。
一、二叉树理论基础
1. 二叉树的种类
a. 满二叉树:
如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
b. 完全二叉树:
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
c. 二叉搜索树:
前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树。
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。
d. 平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_set底层实现是哈希表。
2. 二叉树的存储方式
a. 链式存储
链式存储方式就用指针。
b. 顺序存储
顺序存储的方式就是用数组。
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
3. 二叉树的遍历方式
二叉树主要有两种遍历方式:
a. 深度优先遍历:
先往深走,遇到叶子节点再往回走。包括:
前序遍历(递归法,迭代法):中左右
前序遍历先遍历父结点,再遍历左结点,最后遍历右节点。
void preorder(TreeNode* root) {
visit(root);
preorder(root->left);
preorder(root->right);
}
中序遍历(递归法,迭代法):左中右
中序遍历先遍历左节点,再遍历父结点,最后遍历右节点。
void inorder(TreeNode* root) {
inorder(root->left);
visit(root);
inorder(root->right);
}
后序遍历(递归法,迭代法):左右中
后序遍历先遍历左节点,再遍历右结点,最后遍历父节点。
void postorder(TreeNode* root) {
postorder(root->left);
postorder(root->right);
visit(root);
}
栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的。
a. 广度优先遍历:
一层一层的去遍历。包括:
层次遍历(迭代法)
广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
4. 二叉树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
二、二叉树的递归遍历
1. 144前序遍历
题目:
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例:
输入:root = [1,null,2,3]
输出:[1,2,3]
class Solution {
public:
void preTraversal(TreeNode* root, vector<int>& vec){
if (root == NULL) return;
vec.push_back(root->val);
preTraversal(root->left, vec);
preTraversal(root->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
preTraversal(root, res);
return res;
}
};
2. 145后序遍历
题目:
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
示例:
输入:root = [1,null,2,3]
输出:[3,2,1]
代码:
在这里插入代码片class Solution {
public:
void postTraversal(TreeNode* root, vector<int>& vec){
if (root == NULL) return;
postTraversal(root->left, vec);
postTraversal(root->right, vec);
vec.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
postTraversal(root, res);
return res;
}
};
3. 94中序遍历
题目:
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例:
输入:root = [1,null,2,3]
输出:[1,3,2]
代码:
class Solution {
public:
void inTraversal(TreeNode* root, vector<int>& vec){
if (root == NULL) return;
inTraversal(root->left, vec);
vec.push_back(root->val);
inTraversal(root->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inTraversal(root, res);
return res;
}
};
三、二叉树的非递归遍历(迭代)
昨天看了视频理解了但没自己写,今天依靠理解自己复现一下。
主要通过栈来实现。
前序遍历:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()){
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
if (node->right != NULL) st.push(node->right);
if (node->left != NULL) st.push(node->left);
}
return res;
}
};
后序遍历:
前序得到的是中左右,所以只要调换right和left的入栈顺序,得到的就是中右左,最后reverse数组就得到左右中,即后序遍历。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()){
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
if (node->left != NULL) st.push(node->left);
if (node->right != NULL) st.push(node->right);
}
reverse(res.begin(),res.end());
return res;
}
};
中序遍历:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
//写一下迭代算法试试
vector<int> res;
stack<TreeNode*> st;
TreeNode* cur = root; //用来遍历的指针
while (cur != NULL || !st.empty()){
if (cur != NULL){
st.push(cur);
cur = cur->left;
}
else{
cur = st.top();
st.pop();
res.push_back(cur->val);
cur = cur->right;
}
}
return res;
}
};
总结
二叉树基础要打牢。