二叉树备忘

二叉树基本概念及性质


二叉树(binary tree)是一棵树,每个结点最多有两个儿子(0/1/2),本质上就是图(graph);根据定义,二叉树有以下五种形态:

二叉树的特点如下图:

特殊的二叉树有斜二叉树,满二叉树和完全二叉树,
  • 斜二叉树:每一层只有一个结点,每个结点仅有左子树的称为左斜树,反之称为右斜树;
  • 满二叉树:所有的分支结点都有左子树和右子树,所有的叶结点都处在同一层;
  • 完全二叉树:树中的结点按层编号,与满二叉树对应编号的结点位置相同,即完全二叉树“是满二叉树的一部分”,因此满二叉树一定是完全二叉树,但是完全二叉树不一定是满的;

二叉树可以表示多叉树
在多叉树中,“长子-兄弟法”用firstChild()表示父结点的长子结点【左子树】,<子结点>用nextSibling()依次指向【右子树】,



二叉树的存储结构


二叉树的存储结构分为顺序存储结构(数组)和链式存储结构,其中顺序存储结构只适用于存储完全二叉树

二叉树非递归遍历


1. 先(根)序遍历(“根左右”)

1.2 迭代实现 & 两种版本:
迭代版本一:根结点入栈,根据栈FILO的特性,先遍历的左孩子后入先出,后遍历的右孩子先入后出;


迭代版本二:先自上而下访问左侧链上的节点,将每个左侧链节点的右孩子入栈,再自下而上访问它们的右子树;


2. 中根)序遍历(“左根右”)

2.2 迭代遍历:
借用辅助栈,沿着左侧链下行,自下而上访问左侧链结点,并遍历对应的右子树;
时间复杂度为O(n),空间复杂度线性正比于二叉树的高度,最坏情况下与结点总数相当;

3. 后根)序遍历(“左右”)

3.2 迭代遍历方法:
访问当前结点,遍历以其右兄弟(若存在)为根的子树,向上回溯至父结点(若存在),转入下一个片段;



4. 层序遍历

从根结点开始,自上而下逐层遍历,同一层中从左到右遍历;




二叉树重构

已知先序序列中序序列可确定一棵唯一的二叉树;
已知后序序列中序序列可确定一棵唯一的二叉树;

仅凭借先序序列和后序序列不能确定一棵唯一的二叉树,当左子树或者右子树有空树情况时,无法判断左右子树哪一方为空树,引起歧义,从而重构二叉树不唯一;
重构二叉树为真二叉树(结点的度为0或者2),左右子树同时为空或者不为空,凭借先序序列和后序序列可以确定唯一的真二叉树;



二叉树创建&遍历完整代码部分

 
  1. 前序序列: 1 2 4 7 -1 -1 8 -1 -1 -1 3 5 -1 9 -1 -1 6 -1 -1
  2. 前序遍历: 1 2 4 7 # # 8 # # # 3 5 # 9 # # 6 # #
  3. 中序遍历: # 7 # 4 # 8 # 2 # 1 # 5 # 9 # 3 # 6 #
  4. 后序遍历: # # 7 # # 8 4 # 2 # # # 9 5 # # 6 3 1
  1. /*
  2. * File name:BinaryTreeByList.c
  3. * Description: 前序序列创建二叉树,前序/中序/后序递归遍历二叉树
  4. * Author: yzwall
  5. * Data: 2016/9/19 20:12
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. //////////////////////////////////////////////////////////
  10. typedef int ElementType;
  11. // 二叉树结点结构
  12. typedef struct BiTreeNode {
  13. ElementType element;
  14. struct BiTreeNode *lchild; // 左孩子结点指针
  15. struct BiTreeNode *rchild; // 右孩子结点指针
  16. } BiTreeNode;
  17. //////////////////////////////////////////////////////////
  18. // 前序递归遍历preorder recursive traversal
  19. void traversalPreOrder(BiTreeNode *bt) {
  20. if(bt == NULL) {
  21. printf("# ");
  22. return; // 递归无返回值,可通过return;返回上次递归位置
  23. } else {
  24. printf("%d ", bt->element); // 遍历根结点
  25. traversalPreOrder(bt->lchild); // 遍历左子树
  26. traversalPreOrder(bt->rchild); // 遍历右子树
  27. }
  28. }
  29. // 中序递归遍历inorder recursive traversal
  30. void traversalInOrder(BiTreeNode *bt) {
  31. if(bt == NULL) {
  32. printf("# ");
  33. return;
  34. } else {
  35. traversalInOrder(bt->lchild); // 遍历左子树
  36. printf("%d ", bt->element); // 遍历根结点
  37. traversalInOrder(bt->rchild); // 遍历右子树
  38. }
  39. }
  40. // 后序递归遍历postorder recursive traversal
  41. void traversalPostOrder(BiTreeNode *bt) {
  42. if(bt == NULL) {
  43. printf("# ");
  44. return;
  45. } else {
  46. traversalPostOrder(bt->lchild); // 遍历左子树
  47. traversalPostOrder(bt->rchild); // 遍历右子树
  48. printf("%d ", bt->element); // 遍历根结点
  49. }
  50. }
  51. /*
  52. * 前序输入二叉树结点值(单字符输入,#表示空结点)根据前序输入构造二叉链表表示二叉树
  53. * note: 将结点指针作为返回值,直接将结点指针作为参数传入,传值传递了bt的一个备份,开辟新结点时malloc会赋值给备份
  54. * note: 二级指针可解决该问题,代码可读性差
  55. */
  56. BiTreeNode *createBiTreeFirstOrder() {
  57. BiTreeNode *bt;
  58. ElementType element;
  59. scanf("%d", &element);
  60. if(element == -1) {
  61. bt = NULL; // 空结点
  62. //return; // 递归已有出口return bt, return;会使得空结点值赋值null值失效;
  63. } else {
  64. bt = (BiTreeNode *)malloc(sizeof(BiTreeNode));
  65. if(bt == NULL) {
  66. exit(-1);
  67. printf("BiTreeNode memory allocate failed. ");
  68. } else {
  69. bt->element = element; // 构造根结点
  70. bt->lchild = createBiTreeFirstOrder(); // 构造左子树
  71. bt->rchild = createBiTreeFirstOrder(); // 构造右子树
  72. }
  73. }
  74. return bt;
  75. }
  76. //////////////////////////////////////////////////////////
  77. int main() {
  78. setvbuf(stdout,NULL,_IONBF,0);
  79. setvbuf(stderr, NULL, _IONBF, 0);
  80. BiTreeNode *bt;
  81. bt = createBiTreeFirstOrder(); // 根据前序输入构造二叉树
  82. printf(" 前序遍历: ");
  83. traversalPreOrder(bt); // 前序递归遍历二叉树
  84. printf(" 中序遍历: ");
  85. traversalInOrder(bt); // 前序递归遍历二叉树
  86. printf(" 后序遍历: ");
  87. traversalPostOrder(bt); // 前序递归遍历二叉树
  88. return 0;
  89. }



原文地址:https://www.cnblogs.com/yzwall/p/6637195.html