二叉树遍历之非递归算法

在前一篇文章二叉树遍历递归算法对二叉树遍历的递归算法做了总结,这篇文章就来对二叉树遍历的非递归算法做个汇总。还是与上一篇文章一样的顺序,一一汇总先序、中序、后序以及层序遍历的非递归算法。

1、先序遍历(非递归算法)

先序遍历非递归访问,使用栈即可实现。先序遍历的非递归访问在所有的遍历中算是最简单的了。主要思想就是先将根结点压入栈,然后根结点出栈并访问根结点,而后依次将根结点的右孩子、左孩子入栈,直到栈为空为止。代码如下:

void preOrderIter(struct node *root){if (root == NULL) return;stack<struct node *> s;s.push(root);while (!s.empty()) {struct node *nd = s.top();cout << nd->data << " ";s.pop();if (nd->right != NULL)s.push(nd->right);if (nd->left != NULL)s.push(nd->left);}cout << endl;}

先序遍历的非递归算法另一算法,也是用的栈,只是稍微复杂点,当左子树遍历完后,需要回溯并遍历右子树。

void preOrderIter2(struct node *root){stack<struct node *> s;while (root != NULL || !s.empty()) {if (root != NULL) {cout << root->data << " "; //访问结点并入栈s.push(root); root = root->left; //访问左子树} else {root = s.top(); //回溯至父亲结点s.pop();root = root->right; //访问右子树}}cout << endl;}本算法有一个地方要注意的是,每次从栈中pop出结点时,表示该结点以及该的左子树已经访问完了,接下来访问其右子树。2、中序遍历(非递归算法)

中序遍历非递归算法也是采用栈实现,与上面的先序遍历算法2类似,只是访问根结点的时机不同。

void inOrderIter(struct node *root){stack<struct node *> s;while (root != NULL || !s.empty()) {if (root != NULL) {s.push(root);root = root->left;}else {root = s.top();cout << root->data << " "; //访问完左子树后才访问根结点s.pop();root = root->right; //访问右子树}}cout << endl;}

当然,中序遍历非递归算法还有很多种,比如修改二叉树结点结构加入一个字段来标示结点是否被访问过等,但是都比该算法复杂,且修改了原来的二叉树结构,所以在这里就不啰嗦了,有兴趣的可以移步这里二叉树非递归访问,后续如果有时间我会对二叉树遍历非递归算法通过在结点添加一个标记的方法再做一个总结。

3、后序遍历(非递归算法)

后序遍历的非递归算法较复杂,使用一个栈可以实现,但是过程很繁琐,这里可以巧妙的用两个栈来实现后序遍历的非递归算法。注意到后序遍历可以看作是下面遍历的逆过程:即先遍历某个结点,然后遍历其右孩子,然后遍历其左孩子。这个过程逆过来就是后序遍历。算法步骤如下:

Push根结点到第一个栈s中。

从第一个栈s中Pop出一个结点,并将其Push到第二个栈output中。

然后Push结点的左孩子和右孩子到第一个栈s中。

重复过程2和3直到栈s为空。

完成后,所有结点已经Push到栈output中,且按照后序遍历的顺序存放,直接全部Pop出来即是二叉树后序遍历结果。

void postOrderIter(struct node *root){if (!root) return;stack<struct node*> s, output;s.push(root);while (!s.empty()) {struct node *curr = s.top();output.push(curr);s.pop();if (curr->left)s.push(curr->left);if (curr->right)s.push(curr->right);}while (!output.empty()) {cout << output.top()->data << " ";output.pop();}cout << endl;}4、层序遍历(非递归算法)

如果不考虑分层换行打印,则用一个队列可以很容易的通过非递归实现层序遍历。但是要每打印一层换一行,就显得稍微复杂了一点。可以有两种方法,第一种使用两个队列,代码很简练,第二种方法则是使用一个队列,代码稍显复杂。

方法一:使用两个队列

第一个队列currentLevel用于存储当前层的结点,第二个队列nextLevel用于存储下一层的结点。当前层currentLevel为空时,表示这一层已经遍历完成,可以打印换行符了。

然后将第一个空的队列currentLevel与队列nextLevel交换,然后重复该过程直到结束。这个算法比较好理解。

void levelOrderIter(struct node* root){if (!root) return;queue<struct node *> currentLevel, nextLevel;currentLevel.push(root);while (!currentLevel.empty()) {struct node *currNode = currentLevel.front();currentLevel.pop();if (currNode) {cout << currNode->data << " ";nextLevel.push(currNode->left);nextLevel.push(currNode->right);}if (currentLevel.empty()) {cout << endl;swap(currentLevel, nextLevel);}}}void swap(queue<struct node *> &curr, queue<struct node*> &next){while (!next.empty()) {struct node *nd = next.front();next.pop();curr.push(nd);}}方法二:使用一个队列

只使用一个队列的话,需要额外的两个变量来保存当前层结点数目以及下一层的结点数目。

void levelOrderIter2(struct node *root){if (!root) return;queue<struct node*> nodesQueue;int nodesInCurrentLevel = 1;int nodesInNextLevel = 0;nodesQueue.push(root);while (!nodesQueue.empty()) {struct node *currNode = nodesQueue.front();nodesQueue.pop();nodesInCurrentLevel–;if (currNode) {cout << currNode->data << " ";nodesQueue.push(currNode->left);nodesQueue.push(currNode->right);nodesInNextLevel += 2;}if (nodesInCurrentLevel == 0) {cout << endl;nodesInCurrentLevel = nodesInNextLevel;nodesInNextLevel = 0;}}}

人生有一半掌握在上帝那里,另一半攥在自己的手中。

二叉树遍历之非递归算法

相关文章:

你感兴趣的文章:

标签云: