遍历算法的分类
- 广度优先遍历:即层序遍历。
- 深度优先遍历:分为前序、中序、后序遍历,前、中、后分别代表根节点的遍历位置。 下面先说明前中后序遍历,再说明层序遍历。
深度优先搜索
递归算法
递归算法有三要素,按照三要素来写,可以确保写出正确的递归。
- 确定函数的参数和返回值;
- 确定中止条件;
- 确定单次递归的操作。
迭代算法
迭代算法事实上是用栈来实现递归。前序遍历和后续遍历的代码结构相似,只做出了一点改变。前序和后序遍历代码如下:
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;
}
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;
}
中序遍历迭代算法的变化较大,需要借助一个指针遍历到树的最左侧。中序遍历的代码如下:
vector<int> inorderTraversal(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(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
统一迭代法
前中后序的遍历也有一种统一写法,即将已经访问但还未处理的结点重新放入栈中,并且在后面紧跟着放入一个空结点作为标记,因而这种方法也被称为标记法。统一迭代法的中序遍历代码如下:
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<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}