按顺序挨个得到每个结点。

理想状况是多次调用 next 方法,第一次返回 1,第二次返回 2…

而在之前的设计中,子结点没有保存父结点指针,无法通过 1 访问到 2,所以 Node 添加属性 parent,并将之前的方法全部推翻重写。

#include <stdio.h>
#include <stdlib.h>
#define true 1
#define false 0typedef struct Node *Nodeptr;
typedef int ElementType;
typedef int boolean;// 某个结点
typedef struct Node {// 结点保存的数据ElementType data;// 结点的左子结点Nodeptr left;// 结点的右子结点Nodeptr right;// 结点的父结点Nodeptr parent;
} Node;// 二叉树
typedef struct {// 根结点Nodeptr root;
} Tree;
// 初始化二叉树
void init(Tree *tree) {tree->root = NULL;
}
// 添加结点时的辅助方法,用来找到 node 的位置
void compare(Nodeptr node, Nodeptr root) {if (node->data < root->data) {if (root->left == NULL) {root->left = node;node->parent = root;} else {compare(node, root->left);}} else {if (root->right == NULL) {root->right = node;node->parent = root;} else {compare(node, root->right);}}
}
// 添加结点
boolean add(Tree *tree, ElementType e) {Node *node = calloc(1, sizeof(Node));node->data = e;node->left = NULL;node->right = NULL;if (tree->root == NULL) {tree->root = node;node->parent = NULL;} else {compare(node, tree->root);}return true;
}
// 递归得到所有结点
void printTree(Nodeptr root) {if (root->left != NULL) {printTree(root->left);}    printf("%d ", root->data);if (root->right != NULL) {printTree(root->right);}
}
// 输出所有结点的值
void foreach(Tree tree) {if (tree.root != NULL) {printTree(tree.root);}
}
// 删除结点时的辅助方法,查找被删除结点的位置
boolean find(Tree *tree, ElementType e, Nodeptr dnode) {if (dnode == NULL) {return false;} else {if (e < dnode->data) {find(tree, e, dnode->left);} else if (e > dnode->data) {find(tree, e, dnode->right);} else {Nodeptr link = dnode->right;if (link == NULL) {link = dnode->left;} else {Nodeptr temp = link;while (temp->left != NULL) {temp = temp->left;}temp->left = dnode->left; if (dnode->left != NULL) {dnode->left->parent = temp;}}if (dnode->parent == NULL) {tree->root = link;} else {if (dnode == dnode->parent->left) {dnode->parent->left = link;} else {dnode->parent->right = link;}  if (link != NULL) {link->parent = dnode->parent;}}free(dnode);return true;}}
}
// 删除结点
boolean delete(Tree *tree, ElementType e) {return find(tree, e, tree->root);
}

好,步入正题。

编写 next 方法,第一次调用返回 1,第二次返回 2,第三次返回 3…,大致是左父右的顺序。

第一次调用时,从 root 开始,向左找最小值 1。

node = root;
while (node.left != null) {node = node.left;
}
// 此时 node = 1
// 左

第二次调用,第二小值是 node.parent。

node = node.parent;
// 此时 node = 2
// 父

第三次,node.right 有子结点,说明可能有结点比 node.right 还小,要向左找第三小值。

node = node.right;
// 此时 node = 6
while (node.left != null) {node = node.left;
}
// 此时 node = 3
// 左

第四次,第四小值是 node.parent。

node = node.parent;
// 此时 node = 4
// 父

第五次,node.right 没有子结点,为第五小值。

node = node.right;
// 此时 node = 5
// 右

第六次,在上一步中,返回的 node 是父结点的右结点,说明 node.parent <= node,小值先返回,那 node.parent 肯定在返回 node 之前就返回了,应跳过父结点。

例如 9 是 8 的右结点,则返回 9 之前 8 已经返回过了,9 在向上追溯的过程中,应跳过 8。

同样 8 是 6 的右结点,6 也应跳过。

// 如果 node 是父结点的右结点,跳过父结点,继续向上追溯
while (node == node.parent.right) {node = node.parent;
}
// 此时 node = 4
node = node.parent;
// 此时 node = 6
// 父

由此可见,每次采取的行动与上次 node 的位置有关。

如上次 node 是左结点,则这次应返回父结点。

上次 node 是父结点,则这次的右结点如果没有子结点,就直接返回;如果有子结点,应向左寻找更小的结点。

上次 node 是右结点,则向上寻找未被返回的父结点。

为止我需要两个变量,flag 记录上次结点的位置,node 为上次结点。

// 属性初始化
flag = 始;
node = root;
// next 方法核心代码,根据 flag 选择寻找策略
switch (flag) {    case 始:flag = 左;while (node.left != null) {node = node.left;  }break;case 左:flag = 父;node = node.parent;break;case 父:node = node.right;if (node 没有子结点) {flag = 右;} else {flag = 始;next();}break;// 向上追溯    case 右:flag = 父;while (node 是父结点的右结点) {node = node.parent;}node = node.parent;break;default:break;
}

这里未设置结束条件,其实也很简单,当返回的 node 为最大值时,整个过程结束。

例外 1:

始分支,但 node 没有左结点时,运行以下代码后,node 不变,flag 为左,下次进入左分支返回 null。

flag = 左;
while (node.left != null) {node = node.left;
}

解决:将 flag 改为父,下次就能进入父分支,返回 node.right。

flag = 左;
temp = node;
while (temp.left != null) {temp = temp.left;
}
if (temp == node) {flag = 父;
}
node = temp;

例外 2:

上次返回 6,本次进入父分支,但没有右结点,应向上追溯找到 7,将 flag 改为右,再调用一次 next 方法。

case 父:if (node.right == null) {flag = 右;next();break;}node = node.right;if (node 没有子结点) {flag = 右;} else {flag = 始;next();}break;

综合:

enum location_of_node {START, LEFT, PARENT, RIGHT, END};typedef struct {unsigned int flag : 3;Nodeptr node;Nodeptr maxNode;
} NodeTraversal;void reset(NodeTraversal *nt, Tree tree) {nt->flag = START;Nodeptr temp = tree.root;nt->node = temp;if (temp != NULL) {// 寻找最大值while (temp->right != NULL) {temp = temp->right;}} else {nt->flag = END;}nt->maxNode = temp;
}
boolean hasNext(NodeTraversal nt) {if (nt.flag != END) {return true;}return false;
}
Nodeptr next(NodeTraversal *nt) { Nodeptr temp;switch (nt->flag) {case START:nt->flag = LEFT;temp = nt->node;while (temp->left != NULL) {temp = temp->left;}if (temp == nt->node) {nt->flag = PARENT;}nt->node = temp;break;case LEFT:nt->flag = PARENT;nt->node = nt->node->parent;break;case PARENT:if (nt->node->right == NULL) {nt->flag = RIGHT;next(nt);break;}nt->node = nt->node->right;if (nt->node->left == NULL &&nt->node->right == NULL) {nt->flag = RIGHT;} else {nt->flag = START;next(nt);}break;case RIGHT:nt->flag = PARENT;temp = nt->node;while (temp == temp->parent->right) {temp = temp->parent;}nt->node = temp->parent;break;default :break;}// 如果 node 为最大值,就结束// 之后,再调用 next 方法,将无回应,除非重置if (nt->node == nt->maxNode) {nt->flag = END;}return nt->node;
}

测试程序:

int main() {Tree tree;init(&tree);add(&tree, 1);add(&tree, 2);add(&tree, 3);add(&tree, 4);NodeTraversal nt;reset(&nt, tree);while (hasNext(nt)) {printf("%d ", next(&nt)->data);}printf("\n");// 遍历结束后,需要重置reset(&nt, tree);while (hasNext(nt)) {printf("%d ", next(&nt)->data);}
}

上一篇:二叉树结点的删除

下篇:删除全部结点。

我现在没什么事干,如果有什么问题困扰着你,可以分享给我,让我也困扰一下。

求职,月薪 500 ~ 3000,要求:人少、安静、可思考,不上夜班。

12-二叉树-遍历得到每个结点相关推荐

  1. l2-004 这是二叉搜索树吗?_LeetCode 例题精讲 | 11 二叉树转化为链表:二叉树遍历中的相邻结点...

    本期例题: LeetCode 98. Validate Binary Search Tree 验证二叉搜索树(Medium) LeetCode 426. Convert Binary Tree to ...

  2. SWUST OJ#1052 输出利用先序遍历创建的二叉树中的指定结点的双亲结点

    目录 题目 思路 代码 题目 题目描述 利用先序递归遍历算法创建二叉树并输出该二叉树中指定结点的双亲结点.约定二叉树结点数据为单个大写英文字符.当接收的数据是字符"#"时表示该结点 ...

  3. SWUST OJ 1051: 输出利用先序遍历创建的二叉树中的指定结点的子结点

    先吐槽一下"孩子"居然是禁止使用的词汇?!我说"孩子节点都不行"嘛 题目描述 利用先序递归遍历算法创建二叉树并输出该二叉树中指定结点的儿子结点.约定二叉树结点数 ...

  4. SWUST OJ 1052: 输出利用先序遍历创建的二叉树中的指定结点的双亲结点

    题目描述 利用先序递归遍历算法创建二叉树并输出该二叉树中指定结点的双亲结点.约定二叉树结点数据为单个大写英文字符.当接收的数据是字符"#"时表示该结点不需要创建,否则创建该结点.最 ...

  5. SWUST OJ 1053: 输出利用先序遍历创建的二叉树中的指定结点的度

    水了三题一摸一样的题目,好快乐 题目描述 利用先序递归遍历算法创建二叉树并输出该二叉树中指定结点的度.约定二叉树结点数据为单个大写英文字符.当接收的数据是字符"#"时表示该结点不需 ...

  6. 二叉树遍历/先序输出叶子结点

    二叉树遍历 函数接口定义: void InorderTraversal( BinTree BT ); void PreorderTraversal( BinTree BT ); void Postor ...

  7. 数据结构之二叉树(遍历、建立、深度)

    数据结构之二叉树(遍历.建立.深度) 1.二叉树的深度遍历 二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树的所有结点,使得每个结点被访问一次且仅被访问一次. 对于二叉树的深度遍历,有前序遍历 ...

  8. BinaryTreeTraversal(二叉树遍历)

    二叉树遍历 遍历命名 根据访问结点操作发生位置命名: ① NLR:前序遍历(Preorder Traversal 亦称(先序遍历)) --访问根结点的操作发生在遍历其左右子树之前. ② LNR:中序遍 ...

  9. 九度oj 题目1078:二叉树遍历

    题目1078:二叉树遍历 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5326 解决:3174 题目描述: 二叉树的前序.中序.后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历 ...

  10. 【数据结构-树】2.二叉树遍历与线索二叉树(图解+代码)

    一.二叉树的定义及其主要特征 1.1 二叉树的概念 二叉树是另一种树形结构,其特点是每个结点最多含两棵子树(也就是说,二叉树的度≤2). 二叉树是一种有序树,若将其左.右子树颠倒,则成为另一颗不同的二 ...

最新文章

  1. Windows启动exe应用程序,无法正常启动(0xc000007b)的解决办法
  2. python 拼多多秒杀_关于 拼多多笔试题-简单易懂的秒杀服务
  3. Android Studio如何发布APK
  4. 大厂与小厂工作的选择
  5. PaperSize.RawKind 属性
  6. redis 受攻击怎么办?_最受欢迎的6个最常用的Redis库
  7. 利用spi发送接收信息c语言,SPI接收发送函数程序
  8. JQ js选择节点操作
  9. 在 SSD 上使用 btrfs 文件系统的相关优化
  10. Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_general_ci
  11. 309. 最佳买卖股票时机含冷冻期
  12. excel 查一列字符是否在另一列中出现
  13. dll文件编写、查看和调用
  14. linux资源管理器编写,Linux系统资源管理器.doc
  15. 电子计算机系选课,奥克兰大学计算机系选课
  16. SXF2019长方体的摆放
  17. 物联网推进水产养殖业标准化规模化进程
  18. Flutter之基础Widget
  19. Web测试的各个测试点
  20. Edge浏览器崩溃,错误代码: STATUS_STACK_BUFFER_OVERRUN

热门文章

  1. 创建线程池有哪几种方式?
  2. 接口json参数形式
  3. Evosuite用maven构建(内附详细过程)
  4. plt.subplot()用法
  5. CSDN 正式发布《开发者“湘遇”长沙》报告,为什么一线城市半数开发者向往长沙
  6. OGC标准wms、wmts、wfs等地图服务协议规范 及 arcgis 的永久WMTS 谷歌卫星 卫星影像服务
  7. android跳转到淘宝详情页的小demo
  8. C# 动态创建Access数据库时的错误:“不可识别的数据库格式”
  9. 2019年山东计算机单招学校,2019年山东高职单招学校名单有哪些
  10. 武软职业技术学院计算机分数线,武汉职业技术学院录取分数线2021是多少分(附历年录取分数线)...