1 树的广度优先遍历算法

广度优先遍历算法,又叫宽度优先遍历,或横向优先遍历,是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。

Java实现树的广度优先遍历 树广度优先遍历算法_结点


如上图所示的二叉树,A 是第一个访问的,然后顺序是 B、C,然后再是 D、E、F、G。

那么,怎样才能来保证这个访问的顺序呢?

借助队列数据结构,由于队列是先进先出的顺序,因此可以先将左子树入队,然后再将右子树入队。

这样一来,左子树结点就存在队头,可以先被访问到。

//广度优先遍历

java版本:

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
 
    }
 
}
*/
 
public class Main2{
    public ArrayList<Integer> BfsTree(TreeNode root) {
        ArrayList<Integer> lists = new ArrayList<>();
        if(root == null)
            return lists;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode tree = queue.poll();
            if(tree.left != null)
                queue.offer(tree.left);
            if(tree.right != null)
                queue.offer(tree.right);
            lists.add(tree.val);
        }
        return lists;
    }
}

c++版本

void breadthFirstTravel(Node* root){
queue<Node *> nodeQueue;  //使用C++的STL标准模板库
    nodeQueue.push(root);
    Node *node;
    while(!nodeQueue.empty()){
        node = nodeQueue.front();
        nodeQueue.pop();
        printf(format, node->data);
        if(node->lchild){
            nodeQueue.push(node->lchild);  //先将左子树入队
        }
        if(node->rchild){
            nodeQueue.push(node->rchild);  //再将右子树入队
        }
    }
}

 

1.3 左右交替的层序遍历(使用双端队列)

Java实现树的广度优先遍历 树广度优先遍历算法_压栈_02

c++版本

方法1 使用双端队列

void Z_LeveOrder_duque(struct BtNode* p)
{
	if (NULL == p) return;
	std::deque<struct BtNode*> deq;
	deq.push_front(p);
	deq.push_back(NULL);	// 标志位,划分不同层
	while (!deq.empty())
	{
		struct BtNode* pCur = NULL; 
		if (deq.front() == deq.back()) break;	// 队头 == 队尾 == NULL ,所有元素已经遍历完
		if(deq.back() == NULL)
		{					
			while (deq.front() != NULL)
			{			
				pCur = deq.front();						
				deq.pop_front();	
				std::cout << pCur->data << " ";
				if (pCur->leftchild != NULL)
				{
					deq.push_back(pCur->leftchild);
				}
				if (pCur->rightchild != NULL)
				{
					deq.push_back(pCur->rightchild);
				}
			}

		}
		if (deq.front() == NULL)
		{					
			while (deq.back() != NULL)
			{
				pCur = deq.back();						
				deq.pop_back();	
				std::cout << pCur->data << " ";
				if (pCur->rightchild != NULL)
				{
					deq.push_front(pCur->rightchild);	// 头删尾插
				}
				if (pCur->leftchild != NULL)
				{
					deq.push_front(pCur->leftchild);
				}
			}
		}
	}
	std::cout << std::endl;
}

方法2 :使用两个栈实现

此方法与双端队列实现方法相同,这里就不再赘述。

void Z_LeveOrder_stack(struct BtNode* p)
{
	if (NULL == p) return;
	std::stack<struct BtNode*> st1;
	std::stack<struct BtNode*> st2;
	st1.push(p);
	while (!st1.empty() || !st2.empty())
	{
		while (!st1.empty())
		{
			struct BtNode* pCur = st1.top();
			st1.pop();
			std::cout << pCur->data << " ";
			if (pCur->leftchild != NULL)
			{
				st2.push(pCur->leftchild);
			}
			if (pCur->rightchild != NULL)
			{
				st2.push(pCur->rightchild);
			}
		}
		while (!st2.empty())
		{
			struct BtNode* pCur = st2.top();
			st2.pop();
			std::cout << pCur->data << " ";
			if (pCur->rightchild != NULL)
			{
				st1.push(pCur->rightchild);
			}
			if (pCur->leftchild != NULL)
			{
				st1.push(pCur->leftchild);
			}
		}
	}

	std::cout << std::endl;
}

 方法3 栈与队列实现

这里需要注意的是,栈与队列实现,必须让根结点入栈,然后不论是队列或是栈都以先右孩子后左孩子的顺序存储。栈负责正向从左到右访问,队列负责反向从右向左访问。

void Z_LeveOrder_StackAndQueue(struct BtNode* p)
{
	if (NULL == p) return;
	std::stack<struct BtNode*> st;
	std::queue<struct BtNode*> que;
	st.push(p);		// 入栈
	while (!st.empty() || !que.empty())
	{
		while (!st.empty())
		{
			p = st.top(); st.pop();
			std::cout << p->data << " ";
			if (p->rightchild != NULL)
			{
				que.push(p->rightchild);
			}
			if (p->leftchild != NULL)
			{
				que.push(p->leftchild);
			}
		}
		while (!que.empty())
		{
			p = que.front(); que.pop();
			std::cout << p->data << " ";
			if (p->rightchild != NULL)
			{
				st.push(p->rightchild);
			}
			if (p->leftchild != NULL)
			{
				st.push(p->leftchild);
			}
		}

	}
	std::cout << std::endl;
}

 

2 树的深度优先遍历算法
深度优先遍历算法是遍历算法的一种。是沿着树的深度遍历树的节点。

当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。

如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

Java实现树的广度优先遍历 树广度优先遍历算法_子树_03


如上图所示的二叉树:

A 是第一个访问的,然后顺序是 B、D,然后是 E。接着再是 C、F、G。

那么,怎么样才能来保证这个访问的顺序呢?

分析一下,在遍历了根结点后,就开始遍历左子树,最后才是右子树。

因此可以借助堆栈的数据结构,由于堆栈是后进先出的顺序,由此可以先将右子树压栈,然后再对左子树压栈,

这样一来,左子树结点就存在了栈顶上,因此某结点的左子树能在它的右子树遍历之前被遍历。

//深度优先遍历

java版本:

public void depthTraversal(ListNode node){
       if(node==null){
             System.out.print("empty tree");
             return;
       }
       Stack<ListNode> stack = new Stack<ListNode>();
       stack.push(node);
       while(!stack.isEmpty()){
             ListNode rnode = stack.pop();
             System.out.print(rnode.val);
             if(rnode.right!=null){
                   stack.push(rnode.right);
             }
             if(rnode.left!=null){
                   stack.push(rnode.left);
             }
       }
}

c++版本:

void depthFirstTravel(Node* root){
    stack<Node *> nodeStack;  //使用C++的STL标准模板库
    nodeStack.push(root);
    Node *node;
    while(!nodeStack.empty()){
        node = nodeStack.top();
        printf(format, node->data);  //遍历根结点
        nodeStack.pop();
        if(node->rchild){
            nodeStack.push(node->rchild);  //先将右子树压栈
        }
        if(node->lchild){
            nodeStack.push(node->lchild);  //再将左子树压栈
        }
    }
}