一、二叉树定义
C++定义:
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(): val(0), left(NULL), right(NULL) {}
TreeNode(int x) : val(x), left(NULL), right(NULL){}
TreeNode(int x, TreeNode * left, TreeNode * right) : val(x), left(left), right(right){}
};
Python定义:
class TreeNode:
def __init__(self, value):
self.value = value
self.right = None
self.left = None
Java定义:
public class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) {this.val = val;}
TreeNode(int val, TreeNode left, TreeNode right){
this.val = val;
this.left = left;
this.right = right;
}
}
二、二叉树遍历
二叉树主要有两种遍历方式:
- 深度优先遍历,先走到叶子节点再往回走
a> 前序遍历
b>中序遍历
c>后序遍历
二叉树的前中后序遍历,其中的前中后指的是父节点的访问顺序;使用迭代法来进行前中后序遍历都是借助栈来实现的; - 层序优先遍历,故名思意,一层一层遍历
a>层次遍历
2.1 递归遍历
递归三要素,以前序遍历为例:
-
确定递归函数的参数和返回值:
确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型
void preorder(TreeNode * cur, vector<int>&vec)
-
确定终止条件:
没有终止条件,肯定会出现栈溢出
if (cur == NULL) return;
-
确定单层递归的逻辑:
确定每一层递归需要处理的信息,这里也就会重复调用自己来实现递归的过程
vec.push_back(cur->val); traversal(cur->left, vec); traversal(cur->right, vec);
结合上述三步,二叉树的前序遍历如下:
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;
2.2 迭代遍历
2.2.1 迭代前序遍历
自然使用栈来解决迭代遍历;前序遍历的顺序是:父结点、左孩子、右孩子;那么使用栈来解决,先将父结点入栈,剩余入栈顺序就是右孩子、左孩子;
迭代实例:
- 首先要初始化一个空栈,首先将根节点放入其中;
- 此后进入循环,每次的动作都是弹出当前的栈顶,并且随即访问该节点;
- 而后检查刚被访问的节点是否拥有右孩子,有则令其入栈;
- 接着检查刚被访问的节点是否拥有左孩子,有则令其入栈;
- 循环进入下一步迭代;
class Solution{
pubilc:
vector<int> preorder(TreeNode * root){
stack<TreeNode*> st;
vector<int> res;
if (root == NULL) return res;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return res;
}
};
2.2.2 迭代中序遍历
中序遍历和先序遍历的代码不能经过简单的改动就实现,这是因为,两个遍历首先访问的都是中间节点,但是先序遍历访问到中间节点就进行处理,也就是其访问顺序与处理顺序是一致;而对于中序遍历来说,其访问节点的顺序是左中右,访问到中间节点之后要不断迭代访问到其左子树的最左子结点,然后再对节点进行处理,这也就造成了中序遍历的访问顺序和处理顺序不一致。
要完成二叉树的中序遍历,需要借助指针的遍历来访问节点,栈则用来处理节点上的元素;
- 首先从根节点出发沿着左分支下行,直到最深的节点,这也是全局首先被访问的节点
- 很自然的,要完成二叉树的中序遍历,在利用指针对左侧链进行遍历的过程中,使用栈结构存储左侧链上沿途节点,指针指向到左侧链最深的节点处的左孩子时(也就是NULL),将最后进入栈的元素弹出并处理,观察其是否有右孩子,若有则将指针指向该右孩子,并重复上面的”左侧链遍历”的方式;若没有右孩子,则栈继续弹出并访问节点元素。
- 使用栈结构完成中序遍历的实例如下图所示:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
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;
}
};
2.2.3 迭代后序遍历
先序遍历是中左右,后序遍历是左右中,要完成后序遍历就只需要调整先序遍历,使其编程中右左的遍历顺序,然后反转result数组,输出的结果就是左右中
中左右—改变中序遍历顺序–>中右左—反转result顺序–>左右中
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;
}
};
2.3 层序遍历
层次遍历故名思意就是一层一层来进行遍历,以下图二叉树为例,其层次遍历结果为[F, B, G, A, D, I, C, E, H]
;层次遍历的实现很简单,需要使用到先进先出的队列以及一个变量size来记录每一层节点的个数。
代码如下:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> res;
if(root == NULL){
return res;
}
que.push(root);
while(!que.empty()){
int size = que.size();
vector<int> vec;
for(int i = 0; i< size; i++){
TreeNode* cur = que.front();
que.pop();
vec.push_back(cur->val);
if(cur->left){
que.push(cur->left);
}
if(cur->right){
que.push(cur->right);
}
}
res.push_back(vec);
}
return res;
}
};
三、翻转二叉树
翻转二叉树使用前序遍历或者后序遍历,使用中序遍历比较麻烦,使用层序遍历也是可以的;
翻转二叉树就是在遍历每一个节点时,交换其左子树和右子树;
-
层序遍历代码如下,在对每一层的每一个节点进行遍历时,交换其左孩子和右孩子:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: TreeNode* invertTree(TreeNode* root) { queue<TreeNode *> que; if(root==NULL){ return root; } que.push(root); while(!que.empty()){ int size = que.size(); for(int i = 0; i < size; i++){ TreeNode * cur = que.front(); que.pop(); swap(cur->left, cur->right); if(cur->left){ que.push(cur->left); } if(cur->right){ que.push(cur->right); } } } return root; } };
-
前序迭代遍历:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: TreeNode* invertTree(TreeNode* root) { stack<TreeNode *> st; vector<int> res; if(root == NULL){ return root; } st.push(root); while(!st.empty()){ TreeNode * cur = st.top(); st.pop(); swap(cur->right, cur->left); if(cur->right){ st.push(cur->right); } if(cur->left){ st.push(cur->left); } } return root; } };
-
前序递归遍历:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ 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; } };