1. 前序遍历(preordertraversal):对节点的访问顺序是根、左节点、右节点;
2. 中序遍历(inordertraversal):对节点的访问顺序是左节点、根、右节点;
3. 后序遍历(postordertraversal):对节点的访问顺序是左节点、右节点、根;
4. 层序遍历(LevelOrder Traversal):从上到下按层次遍历该二叉树,对每层从左往右访问各个节点;
对二叉树的定义如下:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
对于遍历,通常有递归和非递归两种写法。以下代码实现的目标是按给定的遍历顺序给出节点的值。
1. 前序遍历(递归):对于每个节点,先访问自己,再访问左子树,最后访问右子树。代码如下:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
preorderRecurTraversal(root, result);
return result;
}
void preorderRecurTraversal(TreeNode* root, vector<int> &result)
{
if(root==NULL) return;
result.push_back(root->val); //visit root node
preorderRecurTraversal(root->left, result); //visit left child
preorderRecurTraversal(root->right, result); //visit right child
}
前序遍历(迭代):使用栈来保存返回的位置(即那些右节点)。
Step 1:创建栈和一个指向root节点的指针,循环以下三步;
Step 2:访问指针指向的节点,并判断它的右节点是否为空,若不为空,则push进栈;
Step 3:判断左节点是否为空,若不为空,移动指针到左节点;
Step 4:若左节点为空,当前节点在step1中已访问过,所以访问右节点。右节点保存在栈中,所以若栈不为空,取栈中元素,并进入下个循环;
代码如下:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode *> nodes;
TreeNode *p = root;
while (p!=NULL) {
result.push_back(p->val);
if (p->right!=NULL) {
nodes.push(p->right);
}
if (p->left!=NULL) {
p = p->left;
} else {
if(nodes.empty()) break; //end
p = nodes.top();
nodes.pop();
}
}
return result;
}
注意的是,while中的判断条件是p!=NULL,而不是判断栈是否为空,因为栈中只保存了右节点(栈为空,也可向左遍历)。且算法结束时,p也是指向最后一个遍历的元素。
也有另一种写法,栈用于定义遍历的顺序,进栈顺序为当前节点,右节点,左节点,出栈顺序为当前节点,左节点,右节点。
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<const TreeNode*> nodes;
if(root != NULL) nodes.push(root);
while(!nodes.empty()){
const TreeNode* n = nodes.top();
nodes.pop();
result.push_back(n->val);
if(n->right != NULL) nodes.push(n->right);
if(n->left != NULL) nodes.push(n->left);
}
return result;
}
2. 中序遍历(递归),代码如下:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
inorderRecurTraversal(root, result);
return result;
}
void inorderRecurTraversal(TreeNode* root, vector<int> &result) {
if(root==NULL) return;
if(root->left != NULL) inorderRecurTraversal(root->left, result);
result.push_back(root->val);
if(root->right != NULL) inorderRecurTraversal(root->right, result);
}
中序遍历(迭代):对于每个节点,先访问左子树,再访问自己,最后访问右子树。
具体来说,先将指针挪到最左的叶子节点,并在此过程中将中间节点保存在栈中。
然后pop栈中节点,访问中间节点,再将指针指向右节点。
迭代终止时,指针指向最右叶节点的right为NULL。
代码如下:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> nodes;
TreeNode* p = root;
while( !nodes.empty() || p!=NULL ){
if(p!=NULL){
nodes.push(p);
p = p->left;
}
else{
p = nodes.top();
result.push_back(p->val);
nodes.pop();
p = p->right;
}
}
return result;
}
3. 后序遍历(递归),代码如下:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
postorderRecurTraversal(root, result);
return result;
}
void postorderRecurTraversal(TreeNode* root, vector<int> &result) {
if(root == NULL) return;
if(root->left !=NULL) postorderRecurTraversal(root->left, result);
if(root->right !=NULL) postorderRecurTraversal(root->right, result);
result.push_back(root->val);
}
后序遍历(迭代):对于每个节点,先访问左子树,再访问右子树,最后访问自己。
栈的作用和中序遍历相同,保存中间节点,同时也定义了节点的输出顺序。
后序遍历的不同在于两点:1.再访问自己之前,确保右子树为空或者已访问;2.根节点最后访问,所以指针p最后指向根节点(意在说明p不能用作循环结束的条件,除非再用个变量专门用于用于栈元素的访问)。
代码如下:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> nodes;
TreeNode* cur = root;
TreeNode* prev = NULL;
do{
while(cur!=NULL){
nodes.push(cur);
cur = cur->left;
}
while(!nodes.empty()){
cur = nodes.top();
// judge whether the right node is NULL or has been visited
if(cur->right==NULL || cur->right==prev){
result.push_back(cur->val);
prev=cur;
nodes.pop();
}else{
cur = cur->right;
break;
}
}
}while(!nodes.empty());
return result;
}
4. 层序遍历(递归):将结果存在二维向量中。
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
levelOrderRecur(root, 1, result);
return result;
}
void levelOrderRecur(TreeNode* root, int level, vector<vector<int>> &result) {
if(root == NULL) return;
if(level > result.size()) {
result.push_back(vector<int>());
}
result[level-1].push_back(root->val);
if(root->left != NULL) levelOrderRecur(root->left, level+1, result);
if(root->right != NULL) levelOrderRecur(root->right, level+1, result);
}
层序遍历(迭代):使用队列(queue)
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> current;
if(root != NULL)current.push(root);
while(!current.empty()) {
int count =current.size();
vector<int> level;
for(int i=0;i<count; i++) {
TreeNode*p = current.front();
level.push_back(p->val);
current.pop();
if(p->left != NULL) current.push(p->left);
if(p->right != NULL) current.push(p->right);
}
result.push_back(level);
}
return result;
}
练习:
前序遍历:https://leetcode.com/problems/binary-tree-preorder-traversal/
中序遍历:https://leetcode.com/problems/binary-tree-inorder-traversal/
后序遍历:https://leetcode.com/problems/binary-tree-postorder-traversal/
层序遍历:https://leetcode.com/problems/binary-tree-level-order-traversal/