二叉树备忘
二叉树基本概念及性质
二叉树(binary tree)是一棵树,每个结点最多有两个儿子(0/1/2),本质上就是图(graph);根据定义,二叉树有以下五种形态:
二叉树的特点如下图:
特殊的二叉树有斜二叉树,满二叉树和完全二叉树,
- 斜二叉树:每一层只有一个结点,每个结点仅有左子树的称为左斜树,反之称为右斜树;
- 满二叉树:所有的分支结点都有左子树和右子树,所有的叶结点都处在同一层;
- 完全二叉树:树中的结点按层编号,与满二叉树对应编号的结点位置相同,即完全二叉树“是满二叉树的一部分”,因此满二叉树一定是完全二叉树,但是完全二叉树不一定是满的;
二叉树可以表示多叉树,
在多叉树中,“长子-兄弟法”用firstChild()表示父结点的长子结点【左子树】,<子结点>用nextSibling()依次指向【右子树】,
二叉树的存储结构
二叉树的存储结构分为顺序存储结构(数组)和链式存储结构,其中顺序存储结构只适用于存储完全二叉树;
二叉树非递归遍历
1. 先(根)序遍历(“根左右”)
1.2 迭代实现 & 两种版本:
迭代版本一:根结点入栈,根据栈FILO的特性,先遍历的左孩子后入先出,后遍历的右孩子先入后出;
迭代版本一:根结点入栈,根据栈FILO的特性,先遍历的左孩子后入先出,后遍历的右孩子先入后出;
迭代版本二:先自上而下访问左侧链上的节点,将每个左侧链节点的右孩子入栈,再自下而上访问它们的右子树;
2. 中(根)序遍历(“左根右”)
2.2 迭代遍历:
借用辅助栈,沿着左侧链下行,自下而上访问左侧链结点,并遍历对应的右子树;
时间复杂度为O(n),空间复杂度线性正比于二叉树的高度,最坏情况下与结点总数相当;
3. 后(根)序遍历(“左右根”)
3.2 迭代遍历方法:
访问当前结点,遍历以其右兄弟(若存在)为根的子树,向上回溯至父结点(若存在),转入下一个片段;
4. 层序遍历
从根结点开始,自上而下逐层遍历,同一层中从左到右遍历;
二叉树重构
已知先序序列和中序序列可确定一棵唯一的二叉树;
已知后序序列和中序序列可确定一棵唯一的二叉树;
仅凭借先序序列和后序序列不能确定一棵唯一的二叉树,当左子树或者右子树有空树情况时,无法判断左右子树哪一方为空树,引起歧义,从而重构二叉树不唯一;
重构二叉树为真二叉树(结点的度为0或者2),左右子树同时为空或者不为空,凭借先序序列和后序序列可以确定唯一的真二叉树;
重构二叉树为真二叉树(结点的度为0或者2),左右子树同时为空或者不为空,凭借先序序列和后序序列可以确定唯一的真二叉树;
二叉树创建&遍历完整代码部分
前序序列: 1 2 4 7 -1 -1 8 -1 -1 -1 3 5 -1 9 -1 -1 6 -1 -1
前序遍历: 1 2 4 7 # # 8 # # # 3 5 # 9 # # 6 # #
中序遍历: # 7 # 4 # 8 # 2 # 1 # 5 # 9 # 3 # 6 #
后序遍历: # # 7 # # 8 4 # 2 # # # 9 5 # # 6 3 1
/*
* File name:BinaryTreeByList.c
* Description: 前序序列创建二叉树,前序/中序/后序递归遍历二叉树
* Author: yzwall
* Data: 2016/9/19 20:12
*/
#include <stdio.h>
#include <stdlib.h>
//////////////////////////////////////////////////////////
typedef int ElementType;
// 二叉树结点结构
typedef struct BiTreeNode {
ElementType element;
struct BiTreeNode *lchild; // 左孩子结点指针
struct BiTreeNode *rchild; // 右孩子结点指针
} BiTreeNode;
//////////////////////////////////////////////////////////
// 前序递归遍历preorder recursive traversal
void traversalPreOrder(BiTreeNode *bt) {
if(bt == NULL) {
printf("#\n");
return; // 递归无返回值,可通过return;返回上次递归位置
} else {
printf("%d\n", bt->element); // 遍历根结点
traversalPreOrder(bt->lchild); // 遍历左子树
traversalPreOrder(bt->rchild); // 遍历右子树
}
}
// 中序递归遍历inorder recursive traversal
void traversalInOrder(BiTreeNode *bt) {
if(bt == NULL) {
printf("#\n");
return;
} else {
traversalInOrder(bt->lchild); // 遍历左子树
printf("%d\n", bt->element); // 遍历根结点
traversalInOrder(bt->rchild); // 遍历右子树
}
}
// 后序递归遍历postorder recursive traversal
void traversalPostOrder(BiTreeNode *bt) {
if(bt == NULL) {
printf("#\n");
return;
} else {
traversalPostOrder(bt->lchild); // 遍历左子树
traversalPostOrder(bt->rchild); // 遍历右子树
printf("%d\n", bt->element); // 遍历根结点
}
}
/*
* 前序输入二叉树结点值(单字符输入,#表示空结点)根据前序输入构造二叉链表表示二叉树
* note: 将结点指针作为返回值,直接将结点指针作为参数传入,传值传递了bt的一个备份,开辟新结点时malloc会赋值给备份
* note: 二级指针可解决该问题,代码可读性差
*/
BiTreeNode *createBiTreeFirstOrder() {
BiTreeNode *bt;
ElementType element;
scanf("%d", &element);
if(element == -1) {
bt = NULL; // 空结点
//return; // 递归已有出口return bt, return;会使得空结点值赋值null值失效;
} else {
bt = (BiTreeNode *)malloc(sizeof(BiTreeNode));
if(bt == NULL) {
exit(-1);
printf("BiTreeNode memory allocate failed.\n");
} else {
bt->element = element; // 构造根结点
bt->lchild = createBiTreeFirstOrder(); // 构造左子树
bt->rchild = createBiTreeFirstOrder(); // 构造右子树
}
}
return bt;
}
//////////////////////////////////////////////////////////
int main() {
setvbuf(stdout,NULL,_IONBF,0);
setvbuf(stderr, NULL, _IONBF, 0);
BiTreeNode *bt;
bt = createBiTreeFirstOrder(); // 根据前序输入构造二叉树
printf("\n前序遍历:\n");
traversalPreOrder(bt); // 前序递归遍历二叉树
printf("\n中序遍历:\n");
traversalInOrder(bt); // 前序递归遍历二叉树
printf("\n后序遍历:\n");
traversalPostOrder(bt); // 前序递归遍历二叉树
return 0;
}
所有博文来自个人为知笔记,内容多为读书笔记和理解内容;