一、前言
很久没有写博客了,主要是因为懒。但是有些东西确实是比较好,值得分享。
二、二叉树的遍历(基础版)
如果学过数据结构的话,应该都了解二叉树的遍历,所以这里对于遍历是什么就不做赘述了。本文的目的是对遍历的算法代码进行回顾和优化。
1、前序遍历
如果我们将一棵二叉树的局部提取出来,所谓前序遍历就是按照“中左右的方式遍历”。有些朋友可能会搞混他们的关系,可以这样理解:什么序遍历就是“中”放的位置。怎么理解呢?如下*
前序遍历:中-->左-->右
中序遍历:左-->中-->右
后序遍历:左-->右-->中
我们可以观察到,不管是什么遍历,“左右”的访问顺序是不变的,唯一变化的是中间节点的访问顺序,所以我们可以知道前序遍历就是中间节点在前面访问的遍历,中序遍历就是中间节点在中间访问的遍历。
之后我们在来看看代码,最普遍的前序遍历方式是递归,代码如下:
C++
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val);
traversal(cur->left, vec);
traversal(cur->right, vec);
}
写成非递归形式就是:
C++
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;
}
2、中序遍历
递归形式:
C++
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec);
vec.push_back(cur->val);
traversal(cur->right, vec);
}
只是讲结果的push顺序改变而已。
非递归形式:
中序遍历非递归的思路就又产生变化了。代码如下:
C++
vector<int> mid_bianli(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();
st.pop();
result.push_back(cur->val);
cur=cur->right;
}
}
return result;
}
3、后序遍历
递归形式:
C++
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec);
traversal(cur->right, vec);
vec.push_back(cur->val);
}
非递归形式:
C++
vector<int> pre_bianli(TreeNode*root){
vector<int> result;
stack<TreeNode*> st;
TreeNode*cur=root;
if(root==NULL)return NULL;
st.push(cur);
while(!st.empty()){
cur=st.top();
st.pop();
if(cur->right)st.push(cur->right);
if(cur->left)st.push(cur->left);
result.push_back(cur->val);
}
return result;
}
非递归形式:这里需要注意的是,前序遍历的顺序是“中左右”,后序遍历的顺序是“左右中”。我们只需要对前序遍历的push顺序从“左右”变成“右左”,结果就变成了“中右左”,之后再反转一下数组,就得到了需要的“左右中”。
三、二叉树的统一遍历法
上面的二叉树遍历方式,前序和后续还好,但是中序就有些不一样了。如果是记忆代码的话,可能就容易记错。因此,为了在手撸代码的时候可以更方便的写出遍历,有牛人想出了二叉树统一遍历法。代码如下:
vector<int> DiedaiBianli(TreeNode*root){
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result
}
这份代码可以通过调整push(node)的顺序来实现前中后遍历,真正实现了一码多用。
四、层次遍历
不多说,代码如下:
vector<vector<int>> cengxu(TreeNode*root){
queue<TreeNode*> que;
vector<vector<int>> result;
if(root!=NULL)
que.push_back(root);
while(!que.empty()){
int size = que.size();
vector<int>temp;
for(int i=0;i<size;i++){
TreeNode* node = que.front();
que.pop();
temp.push_back(node->val);
if(node->left)que.push_back(node->left);
if(node->right)que.push_back(node->right);
}
result.push_back(temp);
}
//reverse(result.begin(), result.end()); 在这⾥反转⼀下数组即可完成自底向上的层序遍历
return result;
}
五、结尾
本次写这份博客的目的不在于解说,而是在于贴出与二叉树有关的代码模板供大家复制粘贴。因为时间关系,几乎只有C++版本的语言。为了适应不同语种人群,我将在后续完善java、python、JavaScript等语言的实现代码。