二叉树基础理论
满二叉树
只有度数为零和度数为二的结点,且度数为零的结点在同一层上。
深度为k,有2^k-1个结点的二叉树
完全二叉树
从满二叉树而来,从右下角水平向左去掉结点而形成的树。
若最底层为第h层,则该层包含[1, 2^(h-1)]个结点。
用完全二叉树实现优先队列(堆排序)——
把优先值最大的元素(根结点)出列,可分为如下步骤:
1.交换首尾两个元素的位置,这样尾元素将会成为根结点,堆有序被打破;
2.剪掉被交换到末尾的原根元素;
3.把交换到根结点的元素下沉到合适位置,重新调整为大顶堆;
4.返回被剪出的元素,即为需要出列的最大优先值元素。
二叉搜索树
若左子树不空,左子树上所有结点值均小于根结点值;
若右子树不空,右子树上所有结点值均大于根结点值。
平衡二叉搜索树(AVL)
带了平衡功能的二叉搜索树
平衡功能:是空树或者左右两子树的高度差绝对值不超过1
map、set、multimap、multiset底层都是AVL
二叉树的遍历方式
前序遍历(递归法)
耳熟能详的做法:
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> res;
Traversal(root, res);
return res;
}
};
前序遍历(迭代法)
递归能做的,栈也能做!
不用递归,使用栈,这样的话,就需要自己考虑逻辑。为什么是先压右结点呢,因为左结点在上,才可以先pop出来啊。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if(root == NULL) return res;
st.push(root);
while(!st.empty()){
TreeNode* tmp = st.top();
st.pop();
res.push_back(tmp->val);
if(tmp->right) st.push(tmp->right);
if(tmp->left) st.push(tmp->left);
}
return res;
}
};
中序遍历(迭代法)
递归法就不贴了哈(换一换顺序的事)
发现中序遍历(迭代)不是把前序遍历(迭代)稍微改下就行得通的了,因为前序要处理的元素(放入结果数组中)和要访问的元素是一样的(都是根结点),但这次不同,中序要处理的元素在最底部,而要访问的元素是一层层的往下,处理顺序和访问顺序不同。需要借助指针。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
TreeNode* cur = root;
while(cur || !st.empty()){
if(cur){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
res.push_back(cur->val);
cur = cur->right;
}
}
return res;
}
};
后序遍历(迭代法)
既然是“左右中”,那么就是“中右左”反过来,我们在前序遍历时是“中左右”,那么只需要将左右结点的入栈顺序换一下就好啦
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
st.pop();
res.push_back(cur->val);
if(cur->left) st.push(cur->left);
if(cur->right) st.push(cur->right);
}
reverse(res.begin(), res.end());
return res;
}
};
二叉树的层序遍历
注意每次队列里进的都是新一层的结点。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> q;
if(root != NULL) q.push(root);
vector<vector<int>> res;
while(!q.empty()){
int s = q.size();
vector<int> v;
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
v.push_back(cur->val);
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
q.pop();
}
res.push_back(v);
}
return res;
}
};
二叉树的层次遍历Ⅱ
就是把上一题的容器翻转一下啦。
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> q;
if(root != NULL) q.push(root);
vector<vector<int>> res;
while(!q.empty()){
int s = q.size();
vector<int> v;
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
v.push_back(cur->val);
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
q.pop();
}
res.push_back(v);
}
reverse(res.begin(), res.end());
return res;
}
};
二叉树的右视图
直接取每一层的最右元素啦
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> q;
if(root != NULL) q.push(root);
vector<int> res;
while(!q.empty()){
int s = q.size();
vector<int> v;
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
v.push_back(cur->val);
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
q.pop();
}
res.push_back(v.back());
}
return res;
}
};
二叉树的层平均值
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> q;
if(root != NULL) q.push(root);
vector<double> res;
while(!q.empty()){
int s = q.size();
double sum = 0;
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
sum += cur->val;
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
q.pop();
}
res.push_back(sum/s);
}
return res;
}
};
N叉树的层序遍历
遍历vector就是遍历数组呀~
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node*> q;
if(root != NULL) q.push(root);
vector<vector<int>> res;
while(!q.empty()){
int s = q.size();
vector<int> v;
for(int i = 0; i < s; i++){
Node* cur = q.front();
v.push_back(cur->val);
for(int j = 0; j < cur->children.size(); j++){
q.push(cur->children[j]);
}
q.pop();
}
res.push_back(v);
}
return res;
}
};
在每个树行中找最大值
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> q;
if(root != NULL) q.push(root);
vector<int> res;
while(!q.empty()){
int s = q.size();
int MAX = INT_MIN;
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
MAX = max(MAX, cur->val);
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
q.pop();
}
res.push_back(MAX);
}
return res;
}
};
填充每个结点的下一个右侧结点指针(Ⅰ和Ⅱ同解)
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> q;
if(root != NULL) q.push(root);
while(!q.empty()){
int s = q.size();
for(int i = 0; i < s; i++){
Node* cur = q.front();
q.pop();
if(i == s-1) cur->next = NULL;
else cur->next = q.front();
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
}
return root;
}
};
以上的八个题目全部都是在层序遍历的基础上稍微修改就可以AC的!太快乐了⑧!
所以说,这些模板题全是层序遍历的子题(还是归到遍历方式这个大的主题下面)
二叉树属性
对称二叉树
比较的是两棵子树的内侧结点和外侧结点。
递归法
class Solution {
public:
bool Compare(TreeNode* left, TreeNode* right){
if(!left && !right) return true;
else if((!left&&right) || (left&&!right)) return false;
else if(left->val != right->val) return false;
bool outside = Compare(left->left, right->right);
bool inside = Compare(left->right, right->left);
return outside && inside;
}
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
return Compare(root->left, root->right);
}
};
迭代法
用栈/队列均可,队列只需要把stack改为queue
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
stack<TreeNode*> st;
st.push(root->right);
st.push(root->left);
while(!st.empty()){
TreeNode* left = st.top(); st.pop();
TreeNode* right = st.top(); st.pop();
if(!left && !right) continue; //左右皆空,继续
if(!left || !right || left->val!=right->val) return false;
st.push(left->right);
st.push(right->left);
st.push(right->right);
st.push(left->left);
}
return true;
}
};
相同的树
递归法
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p && !q) return true;
else if(!q || !p || p->val != q->val) return false;
else return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
迭代法
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p && !q) return true;
queue<TreeNode*> que;
que.push(p); que.push(q);
while(!que.empty()){
TreeNode* right = que.front(); que.pop();
TreeNode* left = que.front(); que.pop();
if(!right && !left) continue;
if(!right || !left || right->val!=left->val) return false;
que.push(left->left);
que.push(right->left);
que.push(left->right);
que.push(right->right);
}
return true;
}
};
二叉树最大深度
递归法
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
if(!root->left && !root->right) return 1;
else return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
迭代法(用队列更方便)
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
queue<TreeNode*> q;
int depth = 0;
q.push(root);
while(!q.empty()){
int s = q.size();
for(int i = 0; i < s; i++){
TreeNode* cur =q.front();
q.pop();
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
depth++;
}
return depth;
}
};
N叉树的最大深度
递归法
class Solution {
public:
int maxDepth(Node* root) {
if(root == NULL) return 0;
int depth = 0;
for(int i = 0; i < root->children.size(); i++){
depth = max(depth, maxDepth(root->children[i]));
}
return depth+1;
}
};
迭代法
class Solution {
public:
int maxDepth(Node* root) {
if(root == NULL) return 0;
queue<Node*> q;
int depth = 0;
q.push(root);
while(!q.empty()){
int s = q.size();
for(int i = 0; i < s; i++){
Node* cur =q.front();
q.pop();
for(int j = 0; j < cur->children.size(); j++){
q.push(cur->children[j]);
}
}
depth++;
}
return depth;
}
};
二叉树最小深度
一排一排的找结点,当发现某个结点没有左右孩子的时候就返回深度。
class Solution {
public:
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
queue<TreeNode*> q;
int depth = 0;
q.push(root);
while(!q.empty()){
depth++;
int s = q.size();
for(int i = 0; i < s; i++){
TreeNode* cur = q.front();
q.pop();
if(!cur->left && !cur->right) return depth;
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
}
return depth;
}
};
完全二叉树的节点个数
递归法
class Solution {
public:
int countNodes(TreeNode* root) {
if(!root) return 0;
if(!root->left && !root->right) return 1;
return countNodes(root->left) + countNodes(root->right) + 1; //别忘了加上根结点的一个
}
};
迭代法
res每次都加一层的结点数(对每次入队入一层的理解更深了呢)
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> q;
if(!root) return 0;
else q.push(root);
int res = 0;
while(!q.empty()){
int s = q.size(); //队列长度要提前确定,因为在变
res += s;
for(int i = 0; i < s; i++){
TreeNode* tmp = q.front(); //在这里取队头(用这层的结点入下一层的结点)
q.pop();
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
}
return res;
}
};
完全二叉树的特性
对于完全二叉树只有两种情况:
① 满二叉树
② 问左右孩子,一旦为满二叉树就按①算(单个结点算满二叉树)
class Solution {
public:
int countNodes(TreeNode* root) {
if(!root) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftDepth = 0, rightDepth = 0;
while(left){
leftDepth++;
left = left->left;
}
while(right){
rightDepth++;
right = right->right;
}
if(leftDepth == rightDepth) return (2<<leftDepth) - 1;
return countNodes(root->left) + countNodes(root->right)+ 1;
}
};
平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
求最大深度的方法好像在此已经不再适用。
深度:从根结点到该结点的最长简单路径边的条数;(从上到下——中左右)
高度:从该结点到叶子结点的最长简单路径边的条数。(从下到上——左右中)
class Solution {
public:
int getHeight(TreeNode* node){
if(!node) return 0;
int leftHeight = getHeight(node->left);
if(leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if(rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : max(leftHeight, rightHeight) + 1;
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
};
二叉树的所有路径
递归法(回溯)
递归和回溯不分家
class Solution {
public:
void findPath(TreeNode* cur, vector<int>& path, vector<string>& res){
path.push_back(cur->val);
if(!cur->left && !cur->right){
string thisPath;
for(int i = 0; i < path.size()-1; i++){
thisPath += to_string(path[i]);
thisPath += "->";
}
thisPath += to_string(path[path.size()-1]);
res.push_back(thisPath);
return;
}
if(cur->left){
findPath(cur->left, path, res);
path.pop_back();
}
if(cur->right){
findPath(cur->right, path, res);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
vector<int> path;
findPath(root, path, res);
return res;
}
};
递归法(隐藏的回溯)
class Solution {
public:
void TravelPath(TreeNode* cur, string path, vector<string>& res){
path += to_string(cur->val);
if(!cur->left && !cur->right){
res.push_back(path);
return;
}
if(cur->left) TravelPath(cur->left, path+"->", res);//隐藏的回溯,因为调用完这个TravelPath之后path的值没有变,只是传参的值变了而已。
if(cur->right) TravelPath(cur->right, path+"->", res);
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
string path;
if(!root) return res;
TravelPath(root, path, res);
return res;
}
};
迭代法
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
stack<TreeNode*> nodest;
stack<string> pathst;
if(!root) return res;
nodest.push(root);
pathst.push(to_string(root->val));
while(!nodest.empty()){
TreeNode* cur = nodest.top(); nodest.pop();
string path = pathst.top(); pathst.pop();
if(!cur->left && !cur->right) res.push_back(path);
if(cur->left){
nodest.push(cur->left);
pathst.push(path + "->" + to_string(cur->left->val));
}
if(cur->right){
nodest.push(cur->right);
pathst.push(path + "->" + to_string(cur->right->val));
}
}
return res;
}
};
二叉树的修改与构造
翻转二叉树
递归法
用的是递归?每个结点的左右子树都反过来,注意NULL的安置。
class Solution {
public:
void Invert(TreeNode* root){
if(root->left && root->right){
TreeNode* tmp = root->left;
root->left = root->right;
root->right = tmp;
Invert(root->left), Invert(root->right);
}else if(root->left){
root->right = root->left;
root->left = NULL;
Invert(root->right);
}else if(root->right){
root->left = root->right;
root->right = NULL;
Invert(root->left);
}else return;
}
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
Invert(root);
return root;
}
};
写得太麻烦啦呜呜/(ㄒoㄒ)/~~
看看官方代码!多么简洁!像话么!
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
迭代法(深度优先)
压入栈之前一定要注意判空
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* tmp = st.top();
st.pop();
swap(tmp->left, tmp->right);
if(tmp->left) st.push(tmp->left);
if(tmp->right) st.push(tmp->right);
}
return root;
}
};
迭代法(广度优先)
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int s = q.size();
for(int i = 0; i < s; i++){
TreeNode* tmp = q.front();
q.pop();
swap(tmp->left, tmp->right);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
}
return root;
}
};
好长呀!呼呼!还是打算分两部分写了,字数有些超标~其实大部分的代码都是模板和套路!
唯一有些精彩的地方就是那个回溯的部份(我觉得之前是没怎么搞懂的)还有就是平衡二叉树的左右中(从下往上遍历)以及完全二叉树利用性质求结点个数那里也是十分精彩的~
总之,继续努力啦!
{纸短情长的爱恋}