瞥数据结构写代码(24) 二叉链表的递归遍历 和 非递归遍历 算法 总结
二叉链表的 遍历 是 二叉链表 各种 操作的 基础,例如 :创建 二叉树;求树的 长度;求树的 叶子节点数;求 节点的 层 数;求 树的 深度,宽度 等等。
总之 不掌握 遍历,就没有 掌握 二叉树;
二叉链表的 遍历 根据 根节点的访问顺序有:先(根节点)序,中(根节点)序,后(根节点)序, 和 层序;
算法思路 有两类:
1. 递归 算法,算法 清晰,容易 证明算法的正确性,但是 效率较低 和 受 系统 栈区 大小的限制,不能 递归太多层次
2.非 递归算法,算法 较复杂一些,但是效率高,又 不受 栈区大小的限制
下面 讲解 这两种 算法:
1.递归算法 ,递归没什么 说的,比较 简单
//递归算法 先序 void preOrderTraverse(Tree tree){ if (tree != NULL) { printf("%d\t",tree->data); preOrderTraverse(tree->leftChild); preOrderTraverse(tree->rightChild); } } //递归算法 中序 void inOrderTraverse(Tree tree){ if (tree != NULL) { inOrderTraverse(tree->leftChild); printf("%d\t",tree->data); inOrderTraverse(tree->rightChild); } } //递归算法 后序 void postOrderTraverse(Tree tree){ if (tree != NULL) { postOrderTraverse(tree->leftChild); postOrderTraverse(tree->rightChild); printf("%d\t",tree->data); } }
2.1 非递归 先序 算法 总共 有 三种 方法:
下面 的两种算法 思路一致,都是 找左子树最左下角节点的同时访问节点,然后 将 节点的 右子树入栈。然后 重复寻找..
void preOrderTraverse1(Tree t){ linkStack stack; stackInit(&stack); stackPush(&stack,t); while (!stackEmpty(stack)) { while (stackGetTop(stack,&t) && t) { printf("%d\t",t->data); stackPush(&stack,t->leftChild); } stackPop(&stack,&t);//NULL 出栈 if (!stackEmpty(stack)) { stackPop(&stack,&t); stackPush(&stack,t->rightChild); } } stackDestory(&stack); } void preOrderTraverse2(Tree tree){ linkStack stack; stackInit(&stack); while (tree || !stackEmpty(stack)) { if (tree) { printf("%d\t",tree->data); stackPush(&stack,tree); tree = tree->leftChild; } else { stackPop(&stack,&tree); tree = tree->rightChild; } } stackDestory(&stack); }第三种 思路 较 前两种 清晰,更加 易懂
1.空树 不操作 2.将 树根入栈 3.访问 根 节点 4. 右子树不为空,将 右子树 入栈 5. 左子树 不为空,将左子树入栈 6.重复 3~ 5 步
比较 奇思妙想的是 先 将 右子树入栈 ,再将 左子树入栈,这样每次 都 先 访问 栈顶 的 左子树。
void preOrderTraverse3(Tree tree){ if (tree != NULL) { linkStack stack; stackInit(&stack); stackPush(&stack,tree); while (!stackEmpty(stack)) { stackPop(&stack,&tree); printf("%d\t",tree->data); if (tree->rightChild != NULL) { stackPush(&stack,tree->rightChild); } if (tree->leftChild != NULL) { stackPush(&stack,tree->leftChild); } } } }
2.2 非递归 中序 有两种算法
算法 思路 同 先序 前两种 算法 的思路 一致,只是 先序是在 在 找 左子树的时候 访问,而终须 是 在 退栈的时候访问
//中序非递归 void inOrderTraverse1(Tree tree){ linkStack stack; stackInit(&stack); stackPush(&stack,tree); while (!stackEmpty(stack)) { while (stackGetTop(stack,&tree) && tree) stackPush(&stack,tree->leftChild); stackPop(&stack,&tree); if (!stackEmpty(stack)) { stackPop(&stack,&tree); printf("%d\t",tree->data); stackPush(&stack,tree->rightChild); } } stackDestory(&stack); } void inOrderTraverse2(Tree tree){ linkStack stack; stackInit(&stack); while (tree || !stackEmpty(stack)) { if (tree) { stackPush(&stack,tree); tree = tree->leftChild; } else { stackPop(&stack,&tree); printf("%d\t",tree->data); tree = tree->rightChild; } } stackDestory(&stack); }
我在网上 研究了 一番,发现了 一种 思路 比较 清晰的算法。
大致 的思路 是 :只有 底下 三种情况的时候 访问 根节点。
1. 节点的 左子树 为空 并且 右 子树 为空 ,2. 刚访问过 右子树 3. 刚访问过左子树 并且 右子树 为空的 时候
这个算法 需要 记住 上一个 访问的 节点 和 利用 先 右子树 入栈 后 左子树 入栈,始终 先访问 左子树。
void postOrderTraverse1(Tree tree){ if (tree != NULL) { linkStack stack; stackInit(&stack); stackPush(&stack,tree); Tree pre = NULL; while (!stackEmpty(stack)) { stackGetTop(stack,&tree); //左右子树都为空,或者 左子树刚访问过,并且 右子树为空,或者 刚访问过 右子树. 可以直接访问 if ((tree->leftChild == NULL && tree->rightChild == NULL) || (pre != NULL && (tree->leftChild == pre || tree->rightChild == pre))) { printf("%d\t",tree->data); stackPop(&stack,&tree); pre = tree; } else { if (tree->rightChild != NULL) { stackPush(&stack,tree->rightChild); } if(tree->leftChild != NULL) { stackPush(&stack,tree->leftChild); } } } } }
2.4 层序 访问, 是 按 从 上 到下,从左 到右 的顺序 访问树。这时候 我们 需要 借助 队列。
这个算法 有一个特点,左子树 一定 在 右子树 前 访问,左子树的 孩子 一定 在 右子树的 孩子前 访问。
大概的思路:1. 将根节点入队列,访问 根节点 2. 将左 子树 入队列, 3.将 右子树 入队列 4.重复 1~3
//层序 //利用队列 void levelOrderTraverse(Tree tree){ if (tree != NULL) { LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (dequeue(&queue,&tree) && tree) { printf("%d\t",tree->data); if (tree->leftChild != NULL) { enqueue(&queue,tree->leftChild); } if (tree->rightChild != NULL) { enqueue(&queue,tree->rightChild); } } queueDestory(&queue); } }
下面 给出 完整 代码,其中 栈 和 队列 的部分代码 没有 贴出,可以 从 我以前的 代码中 摘取。
// binaryTree.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <cstdlib> #include "stack.h" #include "queue.h" typedef int ElementType; typedef struct TreeNode { ElementType data; TreeNode * leftChild; TreeNode * rightChild; } * Tree; void treeInit(Tree * tree){ *tree = NULL; } //其实 自己 不懂 这个 函数 初始化... E_State treeCreate(Tree *tree){ ElementType data; scanf("%d",&data); if (data == 0) { *tree = NULL; }else { *tree = (Tree)malloc(sizeof(TreeNode)); (*tree)->data = data; treeCreate(&(*tree)->leftChild); treeCreate(&(*tree)->rightChild); } return E_State_Ok; } //后序遍历 void treeClear(Tree * tree){ if (tree != NULL) { if ((*tree)->leftChild != NULL) { treeClear(&(*tree)->leftChild); } else if((*tree)->rightChild != NULL) { treeClear(&(*tree)->rightChild); } free(*tree); *tree = NULL; } } void treeDestory(Tree * tree){ treeClear(tree); } //递归算法 先序 void preOrderTraverse(Tree tree){ if (tree != NULL) { printf("%d\t",tree->data); preOrderTraverse(tree->leftChild); preOrderTraverse(tree->rightChild); } } //递归算法 中序 void inOrderTraverse(Tree tree){ if (tree != NULL) { inOrderTraverse(tree->leftChild); printf("%d\t",tree->data); inOrderTraverse(tree->rightChild); } } //递归算法 后序 void postOrderTraverse(Tree tree){ if (tree != NULL) { postOrderTraverse(tree->leftChild); postOrderTraverse(tree->rightChild); printf("%d\t",tree->data); } } //非递归 算法 void preOrderTraverse1(Tree t){ linkStack stack; stackInit(&stack); stackPush(&stack,t); while (!stackEmpty(stack)) { while (stackGetTop(stack,&t) && t) { printf("%d\t",t->data); stackPush(&stack,t->leftChild); } stackPop(&stack,&t);//NULL 出栈 if (!stackEmpty(stack)) { stackPop(&stack,&t); stackPush(&stack,t->rightChild); } } stackDestory(&stack); } void preOrderTraverse2(Tree tree){ linkStack stack; stackInit(&stack); while (tree || !stackEmpty(stack)) { if (tree) { printf("%d\t",tree->data); stackPush(&stack,tree); tree = tree->leftChild; } else { stackPop(&stack,&tree); tree = tree->rightChild; } } stackDestory(&stack); } void preOrderTraverse3(Tree tree){ if (tree != NULL) { linkStack stack; stackInit(&stack); stackPush(&stack,tree); while (!stackEmpty(stack)) { stackPop(&stack,&tree); printf("%d\t",tree->data); if (tree->rightChild != NULL) { stackPush(&stack,tree->rightChild); } if (tree->leftChild != NULL) { stackPush(&stack,tree->leftChild); } } } } //中序非递归 void inOrderTraverse1(Tree tree){ linkStack stack; stackInit(&stack); stackPush(&stack,tree); while (!stackEmpty(stack)) { while (stackGetTop(stack,&tree) && tree) stackPush(&stack,tree->leftChild); stackPop(&stack,&tree); if (!stackEmpty(stack)) { stackPop(&stack,&tree); printf("%d\t",tree->data); stackPush(&stack,tree->rightChild); } } stackDestory(&stack); } void inOrderTraverse2(Tree tree){ linkStack stack; stackInit(&stack); while (tree || !stackEmpty(stack)) { if (tree) { stackPush(&stack,tree); tree = tree->leftChild; } else { stackPop(&stack,&tree); printf("%d\t",tree->data); tree = tree->rightChild; } } stackDestory(&stack); } void postOrderTraverse1(Tree tree){ if (tree != NULL) { linkStack stack; stackInit(&stack); stackPush(&stack,tree); Tree pre = NULL; while (!stackEmpty(stack)) { stackGetTop(stack,&tree); //左右子树都为空,或者 左子树刚访问过,并且 右子树为空,或者 刚访问过 右子树. 可以直接访问 if ((tree->leftChild == NULL && tree->rightChild == NULL) || (pre != NULL && (tree->leftChild == pre || tree->rightChild == pre))) { printf("%d\t",tree->data); stackPop(&stack,&tree); pre = tree; } else { if (tree->rightChild != NULL) { stackPush(&stack,tree->rightChild); } if(tree->leftChild != NULL) { stackPush(&stack,tree->leftChild); } } } } } //层序 //利用队列 void levelOrderTraverse(Tree tree){ if (tree != NULL) { LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (dequeue(&queue,&tree) && tree) { printf("%d\t",tree->data); if (tree->leftChild != NULL) { enqueue(&queue,tree->leftChild); } if (tree->rightChild != NULL) { enqueue(&queue,tree->rightChild); } } queueDestory(&queue); } } int _tmain(int argc, _TCHAR* argv[]) { Tree tree; printf("---------------创建树(先序,0代表子树为空)-----------------\n"); treeCreate(&tree); printf("------------先序递归遍历----------------\n"); preOrderTraverse(tree); printf("------------先序非递归遍历1----------------\n"); preOrderTraverse1(tree); printf("------------先序非递归遍历2----------------\n"); preOrderTraverse2(tree); printf("------------先序非递归遍历3----------------\n"); preOrderTraverse3(tree); printf("\n------------中序递归遍历----------------\n"); inOrderTraverse(tree); printf("\n------------中序非递归遍历1----------------\n"); inOrderTraverse1(tree); printf("\n------------中序非递归遍历2----------------\n"); inOrderTraverse2(tree); printf("\n------------后序递归遍历----------------\n"); postOrderTraverse(tree); printf("\n------------后序非递归遍历1----------------\n"); postOrderTraverse1(tree); printf("\n------------层序遍历----------------\n"); levelOrderTraverse(tree); //及时释放内存是个好习惯.. treeDestory(&tree); return 0; }运行截图: