二叉树
1.特点:
每个节点的度都不超过2,左右子树的顺序不能颠倒。
2.结构(示意图)
如上图:该二叉树共有8个结点,深度为4,叶子节点的个数是4。
深度:最深层次的树的层数。
叶子节点:无子树的结点。
3.二叉树的遍历
以上述二叉树为例:
1.先序遍历:根、左、右——(A-B-D-E-H-C-F-G)
2.中序遍历:左、根、右——(D-B-E-H-A-F-C-G)
3.后序遍历:左、右、根——(D-H-E-B-F-G-C-A)
4.基本操作(递归思想)
1.存储一棵二叉树
给出二叉树的结构体:
typedef struct Tree {
char value;
struct Tree *left;
struct Tree* right;
}Tree;
其实这就相当于是链表,不过有两个指针罢了,将其连接起来即可
2.遍历一棵二叉树(递归遍历)
遍历有先序、中序、后序。
以先序为例:
①遵循根、左、右的顺序。(将每个结点都打印出来)
②递归结尾的标志就是树为空时。
中序与后序同理。
3.求二叉树节点的个数
方法一:遍历思想:遍历途中计数即可。(无论哪种遍历方式)
设置一个全局变量,在遍历过程中计数即可。
方法二:分而治之:结点的个数=左子树的节点个数+右子树的结点个数+1。
4.求二叉树的深度
分而治之的思想:
深度=MAX(左子树的深度,右子树的深度)+根。
5.求二叉树第k层结点的个数
分而治之:左子树的(k-1)层的节点数+右子树的(k-1)层的节点数即可。
6.在二叉树中查找一个节点,找到就返回该节点的地址,找不到就返回空
遍历该二叉树:
①先判断根是否是,根节点若是,直接返回根节点的地址即可。
②如果根节点中没有,就在左子树中找。找到返回。
③如果左子树中没有,就在右子树中找。找到返回
④右子树中也没有,即该二叉树中没有,返回NULL。
5.代码实现:
#include <stdio.h>
#include <stdlib.h>
#include<stdbool.h>
typedef struct Tree {
char value;
struct Tree *left;
struct Tree* right;
}Tree;
//创建结点
Tree *creatNode(char value) {
Tree* tree = (Tree*)malloc(sizeof(Tree));
tree->value = value;
tree->left = NULL;
tree->right = NULL;
return tree;
}
//一.三种遍历二叉树的方式
// 1.先序遍历二叉树
void preOrder(Tree* root) {
//先进行判空
if (root == NULL) {
return;
}
//二叉树不为空,开始先序遍历:根、左、右
printf("%c ", root->value);
preOrder(root->left);
preOrder(root->right);
return;
}
// 2.中序遍历二叉树
void midOrder(Tree* root) {
//先进行判空
if (root == NULL) {
return;
}
//不为空,中序遍历:左、根、右
midOrder(root->left);
printf("%c ", root->value);
midOrder(root->right);
return;
}
// 3.后序遍历二叉树
void subOrder(Tree* root) {
//先进行判空
if (root == NULL) {
return;
}
//非空,进行后序遍历:左、右、根
subOrder(root->left);
subOrder(root->right);
printf("%c ", root->value);
return;
}
int count;//用来计算二叉树中的节点个数
//二.求二叉树的结点个数
// 1.随便哪种遍历方式进行计数即可,以先序为例
int numNode(Tree* root) {
//如果为空,返回0
if (root == NULL) {
return 0;
}
//非空,遍历计数
count++;
numNode(root->left);
numNode(root->right);
return count;
}
// 2.分而治之:左+右+根
int numNode2(Tree* root) {
//新进行判空
if (root == NULL) {
return 0;
}
return numNode2(root->left) + numNode2(root->right) + 1;
}
//三.求二叉树的深度
int depthTree(Tree* root) {
//先进行判空
if (root == NULL) {
return 0;
}
//非空,开始进行计数,MAX(左子树的深度,右子树的深度)+根
int left = depthTree(root->left);
int right = depthTree(root->right);
return (left > right ? left : right) + 1;
}
//四.求二叉树的第k层的结点个数
int kLevelSize(Tree* root,int k) {
//先进行判空
if (root == NULL) {
return 0;
}
if (k == 1) {
return 1;
}
//开始进行计数,左子树的k-1层的节点数+右子树的k-1层的节点数
return kLevelSize(root->left, k - 1) + kLevelSize(root->right, k - 1);
}
//五.查找结点,并返回其地址
Tree* find(Tree* root, char val) {
//先判空
if (root == NULL) {
return NULL;
}
//再判断根节点是否是目标节点
if (root->value == val) {
return root;
}
//根节点不是,查找左子树,左子树没有,再查找右子树
//开始查找左子树
Tree* result = find(root->left,val);
if (result != NULL) {
//说明找到了
return result;
}
//左子树中没找到,在右子树中查找
result = find(root->right, val);
if (result != NULL) {
//说明找到了,返回其地址
return result;
}
//整棵树都查找完了,没找到,返回NULL
return NULL;
}
void test() {
Tree *a = creatNode('A');
Tree *b = creatNode('B');
Tree *c = creatNode('C');
Tree *d = creatNode('D');
Tree *e = creatNode('E');
Tree *f = creatNode('F');
Tree *g = creatNode('G');
Tree *h = creatNode('H');
//构建二叉树
a->left = b; a->right = c;
b->left = d; b->right = e;
e->right = h;
c->left = f; c->right = g;
//一.遍历二叉树
// 1.先序遍历
printf("先序遍历:");
preOrder(a);
// 2.中序遍历
printf("\n中序遍历:");
midOrder(a);
// 3.后序遍历
printf("\n后序遍历:");
subOrder(a);
//二.二叉树的结点个数:
count = 0;
numNode(a);
printf("\n遍历得到二叉树的结点个数为:%d", count);
printf("\n分而治之的思想得到的结点个数为:%d", numNode2(a));
//三.求二叉树的深度
printf("\n该二叉树的深度为:%d", depthTree(a));
//四.求二叉树的第k层的结点个数
printf("\n第3层的结点数目是:%d", kLevelSize(a, 3));
//五.查找
Tree* node = find(a, 'E');
printf("\n查找结点E,它的地址是:%p,它的值是:%c", node,node->value);
printf("\n");
}
int main() {
test();
system("pause");
return 0;
}
总结:
1.对二叉树的操作同链表一样,要先对树进行判空操作
2.分而治之:遇到问题可以将二叉树拆分成左子树和右子树来思考进一步简化问题。
3.递归的边界条件控制:常常是树空。