以下程序测试使用的二叉树:
前序遍历序列:1 2 3 6 8 9 7
中序遍历序列:2 1 6 9 8 3 7
后序遍历序列:2 9 8 6 7 3 1
层序遍历序列:1 2 3 6 7 8 9
二叉树类型定义和访问函数
typedef struct Node { int data; struct Node* lChild; struct Node* rChild; }BiNode, *BiTree; void Visit(BiNode* node) { printf("%d ", node->data); }
【1. 前序遍历-递归】
也可以叫深度遍历,先访问根节点,再依次访问其左、右子树。
//前序遍历,递归(深度遍历) void PreOrderTraverse(BiTree root) { if (root == NULL) return; Visit(root); PreOrderTraverse(root->lChild); PreOrderTraverse(root->rChild); } //前序遍历,递归,减少递归次数。 void PreOrderTraverse_2(BiTree root) { if (root == NULL) return; Visit(root); if (root->lChild) //减少递归次数 PreOrderTraverse(root->lChild); if (root->rChild) PreOrderTraverse(root->rChild); }
【2.1 前序遍历-非递归】
需要用到栈。根节点先入栈,当栈不为空循环,依次取出栈顶结点,访问,将其右、左子树依次入栈。注意要先将右子树入栈,后将左子树入栈,这样在出栈时,先访问左边。
//前序遍历,非递归 void PreOrderTraverse_Loop(BiTree root) { if (root == NULL) return; BiNode* node = NULL; stack<BiNode*> biStack; biStack.push(root); while(!biStack.empty()) { node = biStack.top(); biStack.pop(); Visit(node); if (node->rChild)//右子树先入栈 biStack.push(node->rChild); if (node->lChild) biStack.push(node->lChild); } }
【2.2 前序遍历-非递归】
先将根节点入栈,如果栈不为空,循环:向左走到头,沿途访问结点并入栈,直到最左结点,出栈一个结点,将其右子树入栈。
//前序遍历,非递归 void PreOrderTraverse_Loop_2(BiTree root) { if (root == NULL) return; BiNode* node = NULL; stack<BiNode*> biStack; biStack.push(root); while(!biStack.empty()) { node = biStack.top(); while (node) //向左走到头 { Visit(node); biStack.push(node->lChild); node = node->lChild; } biStack.pop(); // 栈顶NULL出栈 if (!biStack.empty()) { node = biStack.top(); biStack.pop(); biStack.push(node->rChild); } } }
【3. 中序遍历-递归】
先访问左子树,再访问根结点,最后访问右子树。
//中序遍历,递归 void InOrderTraverse(BiTree root) { if (root == NULL) return; InOrderTraverse(root->lChild); Visit(root); InOrderTraverse(root->rChild); }
【4.1 中序遍历-非递归】
如果结点不为空或栈不为空,循环:如果结点不为空,入栈,下一次查看其左子树(沿着左边一直往下,直到其最左结点);如果结点为空,出栈一个结点,访问此结点,下一次查看其右子树。
如果结点为空:
-当前结点是一个左子树结点,此NULL结点的父结点其实没有左子树,将父节点出栈,访问父节点(左子树已完成),接着查看父节点的右子树,右子树并未入栈。
-当前结点是一个右子树结点,此NULL结点的父结点其实没有右子树,此时栈顶是父节点的父节点,代表父节点为根的树遍历完成了,出栈,接着查看父节点的父节点的右子树。
//中序遍历,非递归 void InOrderTraverse_Loop_1(BiTree root) { if (root == NULL) return; stack<BiNode*> biStack; BiNode* node = root; while(node || !biStack.empty()) { if (node) { biStack.push(node); node = node->lChild; } else { node = biStack.top(); biStack.pop(); Visit(node); node = node->rChild; } } }
【4.2 中序遍历-非递归】
与【2.2 前序遍历-非递归】几乎一摸一样,除了Visit() 调用的位置。
//中序遍历,非递归 void InOrderTraverse_Loop_2(BiTree root) { BiNode* node = NULL; stack<BiNode*> biStack; biStack.push(root); while(!biStack.empty()) { node = biStack.top(); while(node) //向左走到尽头 { biStack.push(node->lChild); node = node->lChild; } biStack.pop(); //栈顶NULL结点出栈 if (!biStack.empty()) { node = biStack.top(); biStack.pop(); Visit(node); biStack.push(node->rChild); } }//end of while }
【5. 后序遍历-递归】
先遍历左子树,再遍历右子树,最后访问根结点。
//后序遍历,递归 void PostOrderTraverse(BiTree root) { if (root == NULL) return; PostOrderTraverse(root->lChild); PostOrderTraverse(root->rChild); Visit(root); }
【5.1 后序遍历-非递归】
根节点入栈,如栈不为空,循环:last记住刚访问过的结点;第二个if,将树上所有的节点都按根、右、左的次序入栈;第一个if,如果是last == node->lChild,而由于入栈顺序是根右左,说明node没有右子树,接下来该访问根了;如果是last == node->rChild,说明node的左右子树都访问完了,根据后序遍历的定义,接下来该访问根了。
//后序遍历,非递归 void PostOrderTraverse_Loop_1(BiTree root) { if (root == NULL) return; BiNode* node = NULL; BiNode* last = NULL; stack<BiNode*> biStack; biStack.push(root); while(!biStack.empty()) { node = biStack.top(); biStack.pop(); if (last == node->lChild || last == node->rChild) { //左右子树已经访问完了,该根节点了 Visit(node); last = node; } else if (node->lChild || node->rChild) { //左右子树未访问,当前结点入栈,右、左结点依次入栈 biStack.push(node); if (node->rChild) biStack.push(node->rChild); if (node->lChild) biStack.push(node->lChild); } else // 当前结点是叶子结点,访问 { Visit(node); last = node; } } }
【5.2 后序遍历-非递归】
这个很难理解。。。
//后序遍历,非递归 void PostOrderTraverse_Loop_2(BiTree root) { stack<BiNode*> biStack; BiNode* pre = NULL; while (root != NULL || !biStack.empty()) { if (root != NULL) // 向左走到头 { biStack.push(root); root = root->lChild; } else { root = biStack.top(); //如果右子树还未访问过 if (root->rChild != NULL && pre != root->rChild) { root = root->rChild; } else { root = biStack.top(); biStack.pop(); Visit(root); pre = root; root = NULL; } } } }
【6. 层序遍历】
也叫广度遍历,使用队列实现。
//广度遍历 void LevelOrderTraverse(BiTree root) { if (root == NULL) return; BiNode* node = NULL; queue<BiNode*> biQueue; biQueue.push(root); while(!biQueue.empty()) { node = biQueue.front(); biQueue.pop(); Visit(node); if (node->lChild) biQueue.push(node->lChild); if (node->rChild) biQueue.push(node->rChild); } }