【霍罗维兹数据结构】二叉树前中后序遍历 | 层序遍历 | 复制二叉树 | 判断两个二叉树全等 | 可满足性问题

简介: 【霍罗维兹数据结构】二叉树前中后序遍历 | 层序遍历 | 复制二叉树 | 判断两个二叉树全等 | 可满足性问题

写在前面

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历,就是按照某种特定的规则,一次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。 访问节点所做的操作要看具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其他运算的基础。

Ⅰ. BINARY TREE TRAVERSALS

0x00 概念

经常出现的一个操作是遍历树,也就是说,对树上的每个节点都精确访问一次。

完全遍历会对树中的信息产生一个线性顺序。

当遍历一棵树时,我们希望以同样的方式对待每个节点和它的子树。

假设对于树中的每个节点:

代表向左移动

代表访问该节点(eg. 打印出数据字段)

代表向右移动。

则存在 6 种可能的遍历组合:

—— 中序遍历(inorder traversal)

—— 后序遍历(postorder traversal)

—— 前序遍历(preorder traversal)

二叉树的三种遍历,与表达式的  三种形式,不乏紧密、自然的联系。

(图5.15)

0x01 中序遍历 - Inorder Traversal

void inorder(tree_pointer ptr)
/* inorder tree traversal */
{
  if (ptr) {
    inorder(ptr->left_child);
    printf("%d", ptr->data);
    inorder(ptr->right_child);
  }
}

图5.15 按顺序输出:

0x02 前序遍历 - Preorder Traversal

void preorder(tree_pointer ptr)
/* preorder tree traversal */
{
  if (ptr) {
    printf("%d", ptr->data);
    preorder(ptr->left_child);
    preorder(ptr->right_child);
  }
}

图5.15 按顺序输出:

0x03 后序遍历 - Postorder Traversal

void postorder(tree_pointer ptr)
/* postorder tree traversal */
{
  if (ptr) {
    postorder(ptr->left_child);
    postorder(ptr->right_child);
    printf("%d", ptr->data);
  }
}

图5.15 按顺序输出:

0x04 非递归(循环)中序遍历 - Iterative Inorder Traversal

图5.16 含蓄地表现了 Program5.1 的堆叠和拆垛的过程。

一个没有动作的节点表示该节点被添加到堆栈中, - 而一个有printf动作的节点表示该节点被从堆栈中移除。 注意: - 左边的节点被堆叠起来,直到到达一个空节点, - 然后该节点被从堆叠中移除, - 该节点的右边子节点被堆叠起来

void iter_inorder(tree_pointer node)
{
  int top = -1; /* initialize stack */
  tree_pointer stack[MAX_STACK_SIZE];
  for (; ; ) {
    for (; node; node = node->left_child)
      add(&top, node); /* add to stack */
    node = delete(&top); /* delete from stack */
    if (!node) break; /* empty stack */
    printf("%d", node->data);
    node = node->right_child;
  }
}

🔑 iter_inorder 的分析:令 是树中结点个数。iterInorder 把每个节点入栈一次、出栈一次,所以时间复杂度为 ,空间复杂度取决于树的深度,也是

0x05 层序遍历 - Level Order Traversal

📚 层序遍历(Level Traversal):设二叉树的根节点所在的层数为1的情况下,从二叉树的根节点出发,首先访问第1层的树根节点,然后再从左到右访问第2层上的节点。接着是第3层的节点……以此类推,自上而下、从左向右地逐层访问树的节点。

层序遍历需要利用队列来实现遍历。

void level_order(tree_pointer ptr)
/* level order tree traversal */
{
  int front = rear = 0;
  tree_pointer queue[MAX_QUEUE_SIZE];
  if (!ptr) return; /* empty tree */
  addq(front, &rear, ptr);
  for (; ; ) {
    ptr = deleteq(&front, rear); /*empty list returns NULL*/
    if (ptr) {
      printf("%d", ptr->data);
      if (ptr->left_child)
        addq(front, &rear, ptr->left_child);
      if (ptr->right_child)
        addq(front, &rear, ptr->right_child);
    }
    else break;
  }
}

Ⅱ. 其他二叉树操作

0x00 复制二叉树

根据二叉树的定义,以及前中后序的递归实现,可以很容易地编写其他二叉树操作的C程序。

以常用的二叉树复制操作为例,程序 5.6 中的 copy 函数为实现代码。程序结构与刚才的 postorder 很接近,它实际上是根据它的代码修改而得到的,改动很少。

[Program 5.6] 复制二叉树

tree_pointer copy(tree_pointer original)
/* this function returns a tree_pointer to an exact copy
of the original tree */
{
  tree_pointer temp;
  if (original) {
    temp = (tree_pointer)malloc(sizeof(node));
    if (IS_FULL(temp)) {
      fprintf(stderr, "The memory is full\n");
      exit(1);
    }
    temp->left_child = copy(original->left_child);
    temp->right_child = copy(original->right_child);
    temp->data = original->data;
    return temp;
  }
  return NULL;
}

0x01 判断两个二叉树全等 - Testing For Equality Of Binary Trees

两个全等的二叉树定义为两者的结构相等,并且对应数据域的内容相等。

int equal(tree_pointer first, tree_pointer second)
/* function returns FALSE if the binary trees first and
second are not equal, otherwise it returns TRUE */
{
  return ((!first && !second) || (first && second &&
    (first->data == second->data) &&
    equal(first->left_child, second->left_child) &&
    equal(first->right_child, second->right_child))
}

0x03 可满足性问题(The Satisfiability Problem)

考虑由遍历 和操作符 构成的演算公式集合。

变量只有两种取值 true 和 flase。

公式满足如下条件:

(1)一个变量是一个表达式

(2)如果 x 和 y 是表达式, 则 是表达式

(3)操作符的优先级从高到低为 ,但括号可以改变运算顺序。

这三条基本规则可以生成命题演算的所有演算公式,如 "蕴含" 可用 表示。

对于给定的公式,是否存在一个变量集合的赋值,使该公式的求值结果为 true。

[program 5.8] 求解可满足性问题的第一个程序

for (all 2n possible combinations) {
  generate the next combination;
  replace the variables by their values;
  evaluate the expression;
  if (its value is true) {
    printf(<combination>);
    return;
  }
}
printf("No satisfiable combination\n");

<Evaluating an propositional formula>

为了评为了评估一个表达式,我们可以按后序遍历树,评估子树,直到整个表达式被简化为一个单一的值。 这对应于算术表达式的后缀评估。

[program 5.8] 后序求值

void post_order_eval(tree_pointer node)
{
  /* modified postorder traversal to evaluate
  a propositional calculus tree */
  if (node) {
    post_order_eval(node->left_child);
    post_order_eval(node->right_child);
    switch (node->data) {
    case not : node->value =
      !node->right_child->value;
      break;
    caseand : node->value =
      node->right_child->value && node->left_child->value;
    break;
    case or : node->value =
      node->right_child->value || node->left_child->value;
      break;
    case true: node->value = TRUE;
      break;
    case false: node->value = FALSE;
    }
  }
}

参考资料

Fundamentals of Data Structures in C

相关文章
|
1月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
49 12
|
1月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
46 10
|
1月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
52 2
|
2月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
1月前
|
数据采集 存储 算法
【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】
本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。
46 0
|
3月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
131 4
|
3月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
336 9
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
55 1
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
144 77
|
5天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。

热门文章

最新文章