二叉树的四种遍历:前序、中序、后序、广度

本文详细介绍了二叉树的基本概念、性质、遍历方法及其代码实现。包括前序、中序、后序及广度优先遍历,并提供了递归与非递归的实现方式。

一、基本概念

每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。

性质:

1、非空二叉树的第n层上至多有2^(n-1)个元素。

2、深度为h的二叉树至多有2^h-1个结点。

3、对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,度为2的结点数为n2,则n0 = n2 + 1。

满二叉树:所有终端都在同一层次,且非终端结点的度数为2。

在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。

完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。

对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。

 二、二叉树的遍历

遍历二叉树的所有结点且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。

 

前序遍历:根节点->左子树->右子树(根节点在前面)

中序遍历:左子树->根节点->右子树(根节点在中间)

后序遍历:左子树->右子树->根节点(根节点在后边)

例如:求下面树的三种遍历

 

前序遍历:abdefgc

中序遍历:debgfac

后序遍历:edgfbca

广度遍历:abcdfeg

三、代码实现

struct Node
{
	int val;
	Node * left;
	Node * right;
	Node(int x) :val(x), left(nullptr), right(nullptr){};
};

1、前序遍历:递归与非递归

//递归
void preOrder(Node * root)
{
	if (root != nullptr)
	{
		visit(root);
		preOrder(root->left);
		preOrder(root->right);
	}
}



//迭代
void preOrderF(Node * root)
{
	if (root == nullptr)
		return;
	stack<Node *> s;
	s.push(root);
	Node *p = nullptr;
	while (!s.empty())
	{
		p = s.top();
		s.pop();
		cout << p->val << " ";
		if (p->right)
			s.push(p->right);
		if (p->left)
			s.push(p->left);
	}
}

2、中序遍历

//递归
void inOrder(Node * root)
{
	if (root != nullptr)
	{
		inOrder(root->left);
		visit(root);
		inOrder(root->right);
	}
}



//迭代
void inOrderF(Node * root)
{
	if (root == nullptr)
		return;
	stack<Node *> s;
	Node *p = root;
	while (p||!s.empty())
	{
		if (p)
		{
			s.push(p);
			p = p->left;
		}
		else 
		{
			p = s.top();
			s.pop();
			cout << p->val << " ";
			p = p->right;
		}
	}
	
}

3、后序

递归:

void postOrder(Node * root)
{
	if (root != nullptr)
	{
		postOrder(root->left);
		postOrder(root->right);
		visit(root);
	}
}

三种非递归遍历中,后序遍历是比较难理解的。后序遍历必须保证左子树和右子树都访问完以后才能访问根节点,而能访问根节点的可能是有两种:右子树已经被访问或者当前根节点的右子树为NULL。

给出几种方法:

       A、申请一个栈用于存放节点,申请一个容器vector存放数据(存放倒序的数据),此法空间消耗大点

void postOrderF(Node * root)
{
	if (root == nullptr)
		return;
	stack<Node *> s;
	vector<int> rs;
	s.push(root);
	Node *p = nullptr;
	while (!s.empty())
	{
		p = s.top();
		s.pop();
		rs.insert(rs.begin(),p->val);
		if (p->left)
			s.push(p->left);
		if (p->right)
			s.push(p->right);
	}
	for (int i = 0; i < rs.size(); i++)
		cout << rs[i] << " ";
}

         B、一个标记位pos,当当前根节点右子树和pos相等时,说明已经可以访问该根节点了。

    void _PostOrder_NonR(Node* root)	//非递归--后序
    	{
    		Node* cur = root;
    		Node* pos = root;
    		stack<Node*> s;
    		while (cur || !s.empty())
    		{
    			while (cur)
    			{
    				s.push(cur);
    				cur = cur->_left;
    			}
     
    			Node* top = s.top();
     
    			if (top->_right == NULL || top->_right == pos)
    				//top存的是当前的根节点,当top的右子树为空或者top的右子树为pos,说明右子树已经遍历过,
    				//这时就可以访问当前的根节点了
    			{
    				cout<<top->_data<<" ";
    				pos = top;
    				s.pop();
    			}
    			else
    			{
    				cur = top->_right;
    			}
    		}
    		cout<<endl;
    	}

         C、只用一个栈,两个指针(个人觉得这个好)

对于任一结点p,先将其入栈。若p不存在左孩子和右孩子,则可以直接访问它。或者p存在左孩子或者右孩子,但是左孩子和右孩子都已经被访问过了,则可以直接访问该结点。

若非上述两种情况,则将右孩子和左孩子依次入栈。这样可以保证每次取栈顶元素时,左孩子在右孩子前面被访问,根结点在左孩子和右孩子访问之后被访问。

    //后序遍历的非递归法
    void postOrder(BinaryTreeNode* pRoot)
    {
        stack<BinaryTreeNode*> s;
        BinaryTreeNode *cur;
        BinaryTreeNode *pre=NULL;
        s.push(pRoot);//根结点入栈
        while(!s.empty())
        {
            cur=s.top();
            if((cur->left==NULL&&cur->right==NULL)||(pre!=NULL&&(pre==cur->left||pre==cur->right)))
            {
                //左孩子和右孩子同时为空,或者当前结点的左孩子或右孩子已经遍历过了
                cout<<cur->value<<" ";
                s.pop();
                pre=cur;
            }
            else
            {
                if(cur->right!=NULL)
                    s.push(cur->right);
                if(cur->left!=NULL)
                    s.push(cur->left);
            }
        }
    }

4、广度遍历,剑指offer上

题目:从上到下打印二叉树的每个节点,同层的节点按照从左向右打印。

解析:即分层遍历二叉树。利用广度优先遍历的思想,遍历树或者有向图,都可在队列中完成。

step1:把起始节点放入队列。

step2:每次从队头取出节点,遍历(输出)。接下来,把从该节点能到达的节点(子树或者其他)都依次放入队列。

重复step2,直到队列中的节点为空。

void PrintFromTopToBottom(Node* pRoot)
{
    if(pRoot == NULL)
        return;
 
    std::deque<Node *> deque;
 
    deque.push_back(pRoot);
 
    while(deque.size())
    {
        Node *pNode = deque.front();
        deque.pop_front();
 
        printf("%d ", pNode->m_nValue);
 
        if(pNode->m_pLeft)
            deque.push_back(pNode->m_pLeft);
 
        if(pNode->m_pRight)
            deque.push_back(pNode->m_pRight);
    }
}

参考:

https://blog.youkuaiyun.com/u014465639/article/details/71076092

https://blog.youkuaiyun.com/qq_33951180/article/details/52687692

https://blog.youkuaiyun.com/xiaominkong123/article/details/51567437

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值