目录
树的基本概述
树的概念
节点的度:一个节点含有子树的个数,A的度为6
叶节点:度为0的节点,B,H,I都为叶节点
父节点:如果一个节点含有子节点,那么这个节点就是子节点的父节点
子节点:如果一个节点含有的子数的根节点就为该节点的子节点
兄弟节点:具有相同父节点的节点互为兄弟节点
树的度:这棵树内所有节点中的度的最大值
二叉树的概念及结构
概念:树的度不超过2的树就叫做二叉树
结构:
1. 顺序存储:只有在树为完全二叉树时,才适用于顺序存储,否则会造成空间的浪费
2. 链式存储:由分为二叉链表和三叉链表。二叉链表中,每个节点中包含两个指针,一个指向左孩子,另一个指向右孩子。而三叉链表中,还设置有一个指向父节点的指针。
性质:
1.规定根节点的层数是1,则一棵非空二叉树的第i层最多有个节点
2.规定根节点的层数是1,则深度为h的二叉树最大节点数为
3.对于任意一棵二叉树,如果度为0的节点个数为n0,度为2的节点个数为n2,则满足n0=n2+1
4.规定根节点的层数为1,则n个节点的满二叉树的深度为
遍历:
以下图的二叉树为例
1.深度优先遍历
前序 | 根节点 左子树 右子树 | A B D E C |
中序 | 左子树 根节点 右子树 | D B E A C |
后序 | 左子树 右子树 根节点 | D E B C A |
2.广度优先遍历
核心思路:利用了队列“先进先出”的特点,父节点出队的时候带动子节点入队
1.先将根节点入队
2.父节点出栈的时候带动子节点入队,即A出队,BC入队
3.如此反复(B出队 C出队)
所以,遍历的顺序为A B C D E
满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
二叉树的程序实现
Tree.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
void PrevOrder(BTNode* root);
void InOrder(BTNode* root);
void PostOrder(BTNode* root);
void TreeSize1(BTNode* root,int* psize);
int TreeSize2(BTNode* root);
int TreeLeafSize(BTNode* root);
void LevelOrder(BTNode* root);
Tree.c
1.深度优先遍历
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
else
{
printf(" % c", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
else
{
InOrder(root->left);
printf(" % c", root->data);
InOrder(root->right);
}
}
void PostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
else
{
PostOrder(root->left);
PostOrder(root->right);
printf(" % c", root->data);
}
}
2.广度优先遍历(注意要链接队列的头文件)
void LevelOrder(BTNode* root)
{
//核心思路:上一层出栈的时候带下一层入栈
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q,root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
printf("%c ", front->data);
QueuePop(&q);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
}
3. 计算二叉树节点个数
法一:递归遍历,注意用于计数的变量size,应当传入size的地址psize,才能在多次递归调用函数中正确计数。
//遍历思想
void TreeSize1(BTNode* root, int* psize)
{
if (root == NULL)
{
return;
}
else
{
(*psize)++;
}
TreeSize1(root->left, psize);
TreeSize1(root->right, psize);
}
法二:分治思想,二叉树节点的个数,等于其左子树的节点数 + 右子树的节点数 + 1(根节点)
//分治思想
int TreeSize2(BTNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
return TreeSize2(root->left) + TreeSize2(root->right) + 1;
}
}
4. 计算二叉树叶子节点个数
分治思想:叶子节点数 = 左子树的叶子节点数 + 右子树叶子节点数 (+ 1)(需判断当前节点是否为叶子节点)
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
if (root->left == NULL && root->right == NULL)
{
return TreeLeafSize(root->left) + TreeLeafSize(root->right) + 1;
}
else
{
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
}
}
5.二叉树的销毁
后序遍历销毁,先遍历到叶子节点,再进行销毁节点
void DestoryTree(BTNode* root)
{
//后序销毁
if (root == NULL)
{
return;
}
else
{
DestoryTree(root->left);
DestoryTree(root->right);
free(root);
root = NULL;
}
}
Test.c
因为二叉树没有之前数据结构的增删查改等基本操作,我们可以通过动态内存分配malloc出我们想要的节点,从而组建成我们想要测试的二叉树
#define _CRT_SECURE_NO_WARNINGS
#include"Tree.h"
#include"Queue.h"
void TestTree()
{
BTNode* A = (BTNode*)malloc(sizeof(BTNode));
A->data = 'A';
A->left = NULL;
A->right = NULL;
BTNode* B = (BTNode*)malloc(sizeof(BTNode));
B->data = 'B';
B->left = NULL;
B->right = NULL;
BTNode* C = (BTNode*)malloc(sizeof(BTNode));
C->data = 'C';
C->left = NULL;
C->right = NULL;
BTNode* D = (BTNode*)malloc(sizeof(BTNode));
D->data = 'D';
D->left = NULL;
D->right = NULL;
BTNode* E = (BTNode*)malloc(sizeof(BTNode));
E->data = 'E';
E->left = NULL;
E->right = NULL;
A->left = B;
A->right = C;
B->left = D;
B->right = E;
PrevOrder(A);
printf("\n");
InOrder(A);
printf("\n");
PostOrder(A);
printf("\n");
int Asize = 0;
TreeSize1(A, &Asize);
printf("%d\n", Asize);
printf("%d\n", TreeSize2(A));
int Bsize = 0;
TreeSize1(B, &Bsize);
printf("%d\n", Bsize);
printf("%d\n", TreeSize2(B));
printf("The number of leaf is: %d\n", TreeLeafSize(A));
LevelOrder(A);
}
int main()
{
TestTree();
return 0;
}
树与二叉树oj练习题
二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:输入:root = []
输出:[]
示例 3:输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[1,2]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal
基本思路和之前的代码相似,与之不同的是需要将节点的值放入一个数组中,并且返回数组的地址和长度(输出参数)。
1.由于我们无法得知二叉树节点的个数,所以开辟多大的空间成为了我们面对的第一个问题。我们需要写一个TreeSize函数,得到我们想要的节点个数(即输出参数)
2.由于preorderTraversal的函数开辟了一个内存空间存放数组,所以如果递归该函数,会导致多次开辟空间,所以我们在这再定义了一个子函数_preorderTraversal。通过递归该子函数以达成前序遍历的效果。
int TreeSize(struct TreeNode* root)
{
if (!root)
{
return 0;
}
else
{
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
}
int* _preorderTraversal(struct TreeNode* root, int* a, int* pi)
{
if (root == NULL)
{
return;
}
else
{
a[(*pi)] = root->val;
(*pi)++;
_preorderTraversal(root->left, a, pi);
_preorderTraversal(root->right, a, pi);
}
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
int size = TreeSize(root);
int* a = (int*)malloc(size * sizeof(int*));
int i = 0;
_preorderTraversal(root, a, &i);
*returnSize = size;
return a;
}
二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree
基本思路:分治思想
int maxDepth(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
//保存maxDepth(root->left)的值,减少计算量
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return left > right ? left + 1 : right + 1;
}
}
平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:输入:root = []
输出:true
提示:
树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/balanced-binary-tree
基本思路:分治思想
注意:需要调用前一题最大深度的函数
int maxDepth(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return left > right ? left + 1 : right + 1;
}
}
bool isBalanced(struct TreeNode* root)
{
if (root == NULL)
{
return true;
}
else
{
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return abs(left - right) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
}
二叉树的遍历
描述
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
输入描述:
输入包括1行字符串,长度不超过100。
输出描述:
可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。
示例1
输入:
abc##de#g##f###复制输出:
c b e g d f a来源:牛客网
链接:二叉树遍历_牛客题霸_牛客网
这一题的关键在于,用输入的字符串构建一棵二叉树。
先序构建二叉树:遍历字符串,如果是 '#' ,则说明是空树;如果不是 '#',则二叉树可以分解为,根节点,左子树,右子树三个部分。
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode
{
struct TreeNode* left;
struct TreeNode* right;
char val;
}TNode;
TNode* CreateTree(char *a,int *pi)
{
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
else
{
TNode* root = (TNode*)malloc(sizeof(TNode*));
if (root == NULL)
{
printf("malloc fail\n");
exit(-1);
}
root->val = a[*pi];
(*pi)++;
root->left = CreateTree(a, pi);
root->right = CreateTree(a, pi);
return root;
}
}
void InOrder(TNode *root)
{
if (root == NULL)
{
return;
}
else
{
InOrder(root->left);
printf("%c ", root->val);
InOrder(root->right);
}
}
int main()
{
char str[100] = {};
scanf("%s",str);
int i = 0;
TNode* root = CreateTree(str,&i);
InOrder(root);
return 0;
}