二叉树理论知识基础
满二叉树和完全二叉树
满二叉树是只有度为0或2的节点 且度为0的节点只能在同一层上 如图所示
完全二叉树是除了最后一层的节点可以不被填满 其他层必须填满 如图所示
二叉搜索树和平衡二叉搜索树(红黑树)
二叉搜索树是有数值的 是有序的 如图所示
平衡二叉搜索树 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
二叉树的遍历方式
前序遍历(中左右) 中序遍历(左中右) 后序遍历(左右中)看根的顺序
层次遍历
二叉树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
二叉树的递归遍历
前序遍历 注意遍历的三步
1.确定递归函数的参数和返回值
2.确定遍历终止的条件
3.确定单层递归的逻辑
以下是前序遍历的递归代码 中序 后序只需要将traversal中vec.push_back(cur->val);这段代码变换位置即可
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;
}
};
二叉树的迭代遍历
前序遍历的思路
递归实现的实质就是用栈储存函数的局部变量,参数值和返回地址等 所以迭代遍历我们用栈来实现 首先判断根节点是否为空 若不为空 则压入栈底 接下来用while循环判断当栈一直不为空的时候 弹出栈中的元素 然后依次压入该元素的右孩子和左孩子(注意前序遍历是先压入右孩子,后是左孩子 因为栈是先进后出的数据结构)具体代码如下
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
后序遍历 只需要稍作修改 先压入该元素的左孩子后是右孩子 然后最后再倒置vector
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};
中序遍历则和上面的代码不太一样
可以理解为中序遍历是先一直向左孩子遍历直到为空后 再回到根节点 再遍历到右孩子 所以看下面if语句的逻辑
同样也是用栈来实现 先用一个cur节点代替根节点 然后用while循环当节点不为空(即根节点不为空)或者栈不为空的时候进入循环 然后if语句 如果cur节点不为空 则将cur节点压入栈底然后一直遍历cur的左孩子 然后else语句(即一直遍历左孩子为空了)弹出栈里的元素 输出 然后压入该元素的右孩子入栈 如图为具体代码
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
二叉树的层序遍历
层序遍历的具体过程 就是先根节点 然后弹出根节点 放入两个左右孩子 然后弹出一个孩子 在放入该孩子的两个左右孩子 所有这个逻辑很适合用队列先进先出的数据结构
具体步骤就是 先判断根节点是否为空 若不为空则入队 此时我们需要用int变量size来记录队列里所包含的元素(这样才能知道队列里的元素哪个是属于哪一层的)不能用queue.size()因为这个值是一直在变化的 for循环里知道循环到size这个值 循环里面的逻辑是 用一个临时节点获取队列出口的值 然后弹出 在入队该元素的左孩子和右孩子 具体代码如下
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
226 翻转二叉树
题目思路 只需要在前序遍历的时候 把左右孩子互换一下就可以实现
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr) return root;
swap(root->left,root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
二叉树好难好多 好像还没有到难的部分