深度优先遍历
1、先序遍历
- 访问根节点
- 先序遍历左子树
- 先序遍历右子树
(1)递归算法实现:
template<class T>
void BinaryTree<T>::PreOrder (BinaryTreeNode<T> *root) {
// 前序周游二叉树或其子树
if (root != NULL) {
Visit(root->value()); // 访问当前结点
PreOrder(root->leftchild()); // 前序周游左子树
PreOrder(root->rightchild()); // 前序周游右子树
}
}
递归算法优点:形式简洁,可读性好,正确性容易得到证明,可以给程序的编制和调试带来很大的方便。
递归算法缺点:递归调用比非递归调用消耗的时间与存储空间多,运行的效率较低。
- 所有的递归算法都可转化成相应的非递归算法。
- 将递归算法改成相应的非递归算法需要一个栈来记录调用返回的路径。通过分析递归调用的执行过程,并观察栈的变化就可得到相应的非递归算法。
(2)非递归算法实现:
- 根结点进栈
- 结点出栈,被访问
- 结点的右、左儿子(非空)进栈
- 反复执行 2、3 ,至栈空为止
template<class T>
void BinaryTree<T>::PreOrderWithoutRecursion(BinaryTreeNode<T> *root) {
using std::stack; // 使用STL中的栈
stack<BinaryTreeNode<T>* > aStack;
BinaryTreeNode<T> *pointer = root;
aStack.push(pointer);
while (!aStack.empty()) {
pointer=aStack.top(); // 获得栈顶元素
aStack.pop(); // 栈顶元素退栈
Visit(pointer->value()); // 访问当前结点
if (pointer->rightchild() != NULL) // 非空右孩子入栈
aStack.push(pointer->rightchild());
if (pointer->leftchild() != NULL) // 非空左孩子入栈
aStack.push(pointer->leftchild ()) ;
}
}
2、中序遍历
(1)递归算法实现:
template<class T>
void BinaryTree<T>:: InOrder (BinaryTreeNode<T> *root) {
// 中序周游二叉树或其子树
if (root != NULL) {
InOrder (root->leftchild()); // 中序周游左子树
Visit(root->value()); // 访问当前结点
InOrder(root->rightchild()); // 中序周游右子树
}
}
将二叉树严格地按左子树的所有结点位于根结点的左侧,右子树的所有结点位于根结点的右侧的形式绘制,则对每一个结点做一条垂线,映射到水平线上,由此可得该二叉树的中序遍历结果。
(2)非递归算法实现:
- 每遇到一个结点就把它推入栈,然后去周游其左子树
- 周游完左子树后,从栈顶托出这个结点并访问之
- 然后按照其右链接指示的地址再去周游该结点的右子树
template<class T>
void BinaryTree<T>::InOrderWithoutRecursion(BinaryTreeNode<T> *root) {
using std::stack; // 使用STL中的栈
stack<BinaryTreeNode<T>* > aStack;
BinaryTreeNode<T> *pointer = root;
while (!aStack.empty() || pointer) {
if (pointer) {
aStack.push(pointer); // 当前指针入栈
pointer = pointer->leftchild(); // 左路下降
}else { // 左子树访问完毕,转向访问右子树
pointer = aStack.top(); // 获得栈顶元素
aStack.pop(); // 栈顶元素退栈
Visit(pointer->value()); // 访问当前结点
pointer = pointer->rightchild(); // 指针指向右孩子
}
}
}
3、后序遍历
(1)递归算法实现:
template<class T>
void BinaryTree<T>:: PostOrder (BinaryTreeNode<T> *root) {
// 后序周游二叉树或其子树
if (root != NULL) {
PostOrder(root->leftchild()); // 后序周游左子树
PostOrder (root->rightchild()); // 后序周游右子树
Visit(root->value()); // 访问当前结点
}
}
(2)非递归算法实现:
- 每遇到一个结点,先把它推入栈中,去周游它的左子树
- 周游完它的左子树后,应继续周游该结点的右子树
- 游完右子树之后,才从栈顶托出该结点并访问它
解决方案:
- 由于访问某个结点前需要知道是否已经访问该结点的右子树,因此需要给栈中的每个元素加一个标志位tag
- 标志位用枚举类型Tags表示,tag为Left表示已进入该结点的左子树;tag为Right表示已进入该结点的右子树
enum Tags{Left,Right}; // 定义枚举类型标志位
template <class T>
class StackElement { // 栈元素的定义
public:
BinaryTreeNode<T>* pointer; // 指向二叉树结点的指针
Tags tag; // 标志位
};
template<class T>
void BinaryTree<T>::PostOrderWithoutRecursion(BinaryTreeNode<T>* root) {
using std::stack; // 使用STL的栈
StackElement<T> element;
stack<StackElement<T > > aStack;
BinaryTreeNode<T>* pointer;
if (root == NULL) // 如果是空树则返回
return;
else pointer = root;
while (!aStack.empty() || pointer) {
//如果当前指针非空则压栈并下降到最左子结点
while (pointer != NULL) {
element.pointer = pointer;
element.tag = Left; // 置标志位为Left, 表示进入左子树
aStack.push(element);
pointer = pointer->leftchild();
}
element = aStack.top(); // 获得栈顶元素
aStack.pop(); // 栈顶元素退栈
pointer = element.pointer;
if (element.tag == Left) { // 如果从左子树回来
element.tag = Right; // 置标志位为Right, 表示进入右子树
aStack.push(element);
pointer = pointer->rightchild();
}else { // 如果从右子树回来
Visit(pointer->value()); // 访问当前结点
pointer = NULL; // 置point指针为空,以继续弹栈
}
}
}
深度优先遍历复杂度分析:
- 对于有n个结点的二叉树,周游完树的每一个元素都需要O(n)时间。只要对每个结点的处理(函数Visit的执行)时间是一个常数,那么周游二叉树就可以在线性时间内完成。
- 所需要的辅助空间为周游过程中栈的最大容量,即树的高度。最坏情况下具有n个结点的二叉树高度为n,则所需要的空间复杂度为O(n)。
广度优先遍历
- 首先访问第0层,也就是根结点所在的层
- 然后从左到右依次访问第一层两个结点
- 以此类推,当第i层的所有结点访问完之后,再从左至右依次访问第i+1层的各个结点
队列实现广度优先遍历:
template<class T>
void BinaryTree<T>::LevelOrder (BinaryTreeNode<T> *root){
using std::queue; // 使用STL的队列
queue<BinaryTreeNode<T>*> aQueue;
BinaryTreeNode<T> *pointer = root;
if (pointer)
aQueue.push(pointer); // 根结点入队列
while (!aQueue.empty()) { // 队列非空
pointer = aQueue.front(); // 获得队列首结点
aQueue.pop(); // 当前结点出队列
Visit(pointer->value()); // 访问当前结点
if (pointer->leftchild() != NULL)
aQueue.push(pointer->leftchild()); // 左子树进队列
if (pointer->rightchild()!= NULL)
aQueue.push(pointer->rightchild()); // 右子树进队列
}
}