1.递归法
递归法的三要素:
- 确定函数的参数和返回值
- 确定递归的终止条件
- 每一层递归的逻辑
那么对于这个三种不同的遍历方式,递归也有所不同。唯一不同的就是对应的一个每一层的递归逻辑
//先序遍历
void traversal(TreeNode* cur,vector<int>& vec){//参数和返回值
if(cur == nullptr){//终止条件
return;
}
vec.push_back(cur->val);//递归逻辑
traversal(cur->left,vec);
traversal(cur->right,vec);
}
//中序遍历
void traversal(TreeNode* cur,vector<int>& vec){
if(cur == nullptr){
return;
}
traversal(cur->left,vec);
vec.push_back(cur->val);
traversal(cur->right,vec);
}
//后序遍历
void traversal(TreeNode* cur,vector<int>& vec){
if(cur == nullptr){
return;
}
traversal(cur->left,vec);
traversal(cur->right,vec);
vec.push_back(cur->val);
}
vector<int> preorderTraversal(TreeNode* root) {
//第一种方法:使用递归进行遍历
/*
vector<int> ret;
traversal(root,ret);
return ret;*/
}
2.迭代法
为什么可以使用迭代法来完成遍历呢?因为递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
先序遍历和后序遍历有相同之处,可以都看作是先序遍历,只是顺序不一样罢了,后序遍历还多了一个翻转的操作。
中序遍历会有所不同,因为中序遍历的时候,我们遇到的cur不是遇到了就输出,需要先输出左边的,所以相对来说要麻烦点,要找到最底层的那个节点之后再进行操作。
vector<int> res;
stack<TreeNode*> stk;
while (root != nullptr || !stk.empty()) {
while (root != nullptr) {
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
//先序遍历
vector<int> preorderTraversal(TreeNode* root) {
//第二种方法:使用迭代的方法进行操作,使用堆栈
vector<int> ret;
if(root == nullptr){return ret;}
stack<TreeNode*> stack;
stack.push(root);
while(!stack.empty()){
TreeNode* tmp = stack.top();
stack.pop();
ret.push_back(tmp->val);
if(tmp->right!=nullptr) stack.push(tmp->right);
if(tmp->left!=nullptr) stack.push(tmp->left);
}
return ret;
}
//后序遍历
vector<int> postorderTraversal(TreeNode* root) {
//第二种方法:使用迭代法来进行遍历
vector<int> ret;
if(root == nullptr){return ret;}
stack<TreeNode*> stack;
stack.push(root);
while(!stack.empty()){
TreeNode* tmp = stack.top();
stack.pop();
ret.push_back(tmp->val);
if(tmp->left!=nullptr) stack.push(tmp->left);//两个之间的差别
if(tmp->right!=nullptr) stack.push(tmp->right);
}
reverse(ret.begin(),ret.end());//加上一个翻转
return ret;
}
3.一致迭代法
统一迭代法的思路:我们先按照遍历的反向顺序进行操作入栈,那么后面出栈的时候就是我们想要的结果。对于需要进行特殊操作的地方:中间节点,因为这个不是直接遍历到就输出的,所以要对其进行特殊操作,在后面加一个空的节点。
作用:当我们遍历到空节点的时候,代表这个节点的下一个节点要被输出了。从而完成统一的操作。
//中序遍历
vector<int> inorderTraversal(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(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
if (node->right) st.push(node->right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.top(); // 重新取出栈中元素
st.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
//先序遍历
vector<int> preorderTraversal(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();
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
st.push(node); // 中
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
//后序遍历
vector<int> postorderTraversal(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;
}