二叉树的前序、中序、后序和层序遍历

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/


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值