第一次头条面试的时候就遇到树的问题了。之后,树类型题就变成了我的一个执念。
树的问题面试很容易考到,因为其特殊的结构,使得各种思想在其中应用的淋漓尽致。
究其根本,其实树的问题大都逃不出树的四种遍历:前序遍历,中序遍历,后序遍历与层序遍历思想。
前三种遍历指明了树的本质——递归,层序遍历则是BFS的核心思想。
(还是因为执念,所以我选的题都是牛客网上的例题。在读入数据时,使用tree[N][2]作为存储结构,其中tree[N][0]表示左子树,tree[N][1]表示右子树)
前序遍历
//递归
void preorderCur( int root_val) {
if(root_val == 0) return;
cout<<root_val<<" ";
preorderCur( tree[root_val][0]);
preorderCur( tree[root_val][1]);
}
//非递归
void preorder(int root_val) {
stack<int> st;
if(root_val != 0) st.push(root_val);
while(!st.empty()) {
int val = st.top(); st.pop();
cout<<val<<" ";
if(tree[val][1] != 0) st.push(tree[val][1]);
if(tree[val][0] != 0) st.push(tree[val][0]);
}
}
中序遍历
//递归
void inorderCur(int root_val) {
if(root_val == 0) return;
inorderCur( tree[root_val][0]);
cout<<root_val<<" ";
inorderCur( tree[root_val][1]);
}
//非递归
void inorder(int root_val) {
stack<int> st;
int val = root_val;
while(!st.empty() || val != 0)
{
if(val != 0) {
st.push(val);
val = tree[val][0];
} else
{
val = st.top(); st.pop();
cout<<val<<" ";
val = tree[val][1];
}
}
}
后序遍历
void posorderCur(int root_val) {
if(root_val == 0) return;
posorderCur(tree[root_val][0]);
posorderCur(tree[root_val][1]);
cout<<root_val<<" ";
}
后序遍历的非递归方法还蛮复杂的,可以说是屡看屡忘。
唯一一个我能记住的,先按照类似前序遍历的方法,遍历出 1 3 2,之后取反变为 2 3 1即可。
层序遍历:
N叉树的层序遍历
vector<vector<int>> levelOrder(Node* root) {
if(!root) return {};
vector<vector<int>> res;
queue<Node*> q;
q.push(root);
while(!q.empty()) {
int size = q.size();
vector<int> vec;
for(int i = 0; i < size; i++) {
Node* node = q.front(); q.pop();
vec.push_back(node->val);
for(Node* a : node->children) {
q.push(a);
}
}
res.push_back(vec);
}
return res;
}
遍历总结:
遍历的递归方法简单又精妙。
第一次上数据结构课时,只觉得简单,没有深入思考而是直接把代码背下来。
直到最近做题时,才发现大多数树的问题,都是三种遍历或者层序遍历的进一步拓展,核心思想基本不变,值得好好总结归纳一下。