看数据结构写代码(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.重点 讲解 非 递归算法,非递归 算法 需要 借助 栈 (先,中,后序) 或者 队列(层序) 来进行遍历。

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);}

2.3 后序遍历 是 所有遍历中 最难的 一种我在网上 研究了 一番,发现了 一种 思路 比较 清晰的算法。

大致 的思路 是 :只有 底下 三种情况的时候 访问 根节点。

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 层序 访问, 是 按 从 上 到下,从左 到右 的顺序 访问树。这时候 我们 需要 借助 队列。

这个算法 有一个特点,左子树 一定 在 右子树 前 访问,左子树的 孩子 一定 在 右子树的 孩子前 访问。

为你的快乐而快乐的是朋友,为你的难过而难过的才是你的知己。

看数据结构写代码(24) 二叉链表的递归遍历 和 非递归遍历 算法

相关文章:

你感兴趣的文章:

标签云: