1.链式二叉树的实现
⽤链表来表示⼀棵⼆叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址 ,其结构如下:
cpp
typedef int BTDataType;
typedef struct BinTreeNode
{
BTDataType val;
struct BinTreeNode* left;
struct BinTreeNode* right;
}BTNode;
创建⼀棵链式⼆叉树:
cpp
BTNode* BuyBTNode(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL)
{
perror("malloc fail!");
return;
}
newnode->val = x;
newnode->left = newnode->right = NULL;
return newnode;
}
//手搓一棵树
BTNode* CreatTree() {
BTNode* n1 = BuyBTNode(1);
BTNode* n2 = BuyBTNode(2);
BTNode* n3 = BuyBTNode(3);
BTNode* n4 = BuyBTNode(4);
BTNode* n5 = BuyBTNode(5);
BTNode* n6 = BuyBTNode(6);
n1->left = n2;
n1->right = n4;
n2->left = n3;
n4->left = n5;
n4->right = n6;
}
⼆叉树分为空树和⾮空⼆叉树,⾮空⼆叉树由根结点、根结点的左⼦树、根结点的右⼦树组成的
根结点的左⼦树和右⼦树分别⼜是由⼦树结点、⼦树结点的左⼦树、⼦树结点的右⼦树组成的,因此⼆叉树定义是递归式的,后序链式⼆叉树的操作中基本都是按照该概念实现的。
2.前中后序遍历
⼆叉树的操作离不开树的遍历,⼆叉树的遍历有哪些⽅式:
遍历规则:
按照规则,⼆叉树的遍历有:前序/中序/后序的递归结构遍历
1)前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前
访问顺序为:根结点、左⼦树、右⼦树
2)中序遍历(Inorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)
访问顺序为:左⼦树、根结点、右⼦树
3)后序遍历(Postorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之后
访问顺序为:左⼦树、右⼦树、根结点

前序 :1 2 3 N N N 4 5 N N 6 N N
中序 :N 3 N 2 N 1 N 5 N 4 N 6 N
后序 :N N 3 N 2 N N 5 N N 6 4 1
层序 :1 2 4 3 5 6


cpp
//前序遍历
void PreOrder(BTNode* root) {
if (root == NULL)
return;
printf("%d ", root->val);
PreOrder(root->left);
PreOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root) {
if (root == NULL)
return;
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTNode* root) {
if (root == NULL)
return;
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
3 结点个数以及高度等
cpp
////存在线程安全的风险
////int size = 0;
//void TreeSize(BTNode* root,int* psize) {
// if (root == NULL)
// return;
// else
// ++(*psize);
// TreeSize(root->left,psize);
// TreeSize(root->right,psize);
//}
int TreeSize(BTNode* root) {
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
int TreeHeight(BTNode* root) {
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
/第K层节点个数
//分治:
//1.递归子问题:当前树的第K层个数 == 左子树的第K-1层个数 + 右子树的第K-1层个数
//2.返回问题(最小子问题):非空&&K==1
int TreeKLevel(BTNode* root, int k) {
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}
//查找x所在的节点
BTNode* TreeFind(BTNode* root, int x) {
if (root == NULL)
return NULL;
if (root->val == x)
return root;
BTNode* left = TreeFind(root->left, x);
if (left != NULL)
return left;
BTNode* right = TreeFind(root->right, x);
if (right != NULL)
return right;
return NULL;
}
//二叉树的销毁
void TreeDestroy(BTNode* root)
{
if (root == NULL)
return;
TreeDestroy(root->left);
TreeDestroy(root->right);
free(root);
//root = NULL;这个root置空不管用,要么传二级指针,要么在函数调用完成后置空
}
4 .层序遍历
除了先序遍历、中序遍历、后序遍历外,还可以对⼆叉树进⾏层序遍历。设⼆叉树的根结点所在层数为1,层序遍历就是从所在⼆叉树的根结点出发,⾸先访问第⼀层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历。
实现层序遍历需要额外借助数据结构:队列

cpp
//层序遍历
void LevelOrder(BTNode* root)
{
Que q;
QueueInit(&q);
if(root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->val);
//带入下一层
if(front->left)
QueuePush(&q, front->left);
if(front->right)
QueuePush(&q, front->right);
}
printf("\n");
QueueDestroy(&q);
}
5. ⼆叉树算法题
5.1 单值⼆叉树
cpp
bool isUnivalTree(struct TreeNode* root) {
if(root == NULL)
{
return true;
}
if(root->left && (root->val != root->left->val)){
return false;
}
if(root->right && (root->val != root->right->val)){
return false;
}
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
5.2 相同的树
cpp
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
//都为空
if(p == NULL && q == NULL)
{
return true;
}
//其中一个为空
if((p == NULL || q == NULL) ){
return false;
}
if(p->val == q->val){
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}else{
return false;
}
}
拓展学习:
cpp
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
//都为空
if(p == NULL && q == NULL)
{
return true;
}
//其中一个为空
if((p == NULL || q == NULL) ){
return false;
}
//镜像,左节点与右节点比较,右节点与左节点比较
if(p->val == q->val){
return isSameTree(p->left, q->right) && isSameTree(p->right, q->left);
}else{
return false;
}
}
bool isSymmetric(struct TreeNode* root) {
if(root == NULL)
return true;
return isSameTree(root->left, root->right);
}
5.3 另⼀棵树的子树
cpp
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
//都为空
if(p == NULL && q == NULL)
{
return true;
}
//其中一个为空
if((p == NULL || q == NULL) ){
return false;
}
if(p->val == q->val){
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}else{
return false;
}
}
//查找+数的比较
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) {
if(root == NULL)
return false;
if(root->val == subRoot->val && isSameTree(root, subRoot))
{
return true;
}
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
5.4 ⼆叉树遍历
前序遍历:
cpp
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}
void preorder(struct TreeNode* root, int* a,int* i){
if(root == NULL)
return;
a[(*i)++] = root->val;
preorder(root->left,a,i);
preorder(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = TreeSize(root);
int* a = (int*)malloc(sizeof(int)*(*returnSize));
int i = 0;
preorder(root,a,&i);
return a;
}
5.5 二叉树的构建及遍历
cpp
#include<stdio.h>
typedef struct BinTreeNode{
char val;
struct BinTreeNode* left;
struct BinTreeNode* right;
}BTNode;
BTNode* CreateTree(char* a,int* pi){
if(a[*pi] == '#')
{
(*pi)++;
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
newnode->val = a[(*pi)++];
newnode->left = CreateTree(a,pi);
newnode->right = CreateTree(a,pi);
return newnode;
}
void InOrder(BTNode* root) {
if (root == NULL)
return;
InOrder(root->left);
printf("%c ", root->val);
InOrder(root->right);
}
int main() {
char a[100];
scanf("%s",a);
int i = 0;
BTNode* root = CreateTree(a,&i);
InOrder(root);
return 0;
}
6.判断是否为完全二叉树
cpp
//判断完全二叉树
bool TreeIsComplete(BTNode* root)
{
Que q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
//遇到空以后就跳出,后续判断
if (front == NULL)
break;
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
//如果剩下的节点都是空,就是完全二叉树,存在非空就不是完全二叉树
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front != NULL)
{
QueueDestroy(&q);
return false;
}
}
return true;
}
7.二叉树选择题
⼆叉树性质
对任何⼀棵⼆叉树, 如果度为 0 其叶结点个数为 , 度为 2 的分⽀结点个数为 ,则有n0 = n2 +1
根据⼆叉树的性质:
cpp
1. 某⼆叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该⼆叉树中的叶⼦结点数为(B )
A 不存在这样的⼆叉树
B 200
C 198
D 199
2.在具有 2n 个结点的完全⼆叉树中,叶⼦结点个数为(A )
A n
B n+1
C n-1
D n/2
解:度为0是N0,度为1是N1,度为2是N2
则 N0 + N1 + N2 = 2n
由二叉树性质 N0 = N2 + 1 得
N0 + N1 + N0 - 1 = 2n
2N0 + N1 - 1 = 2n
由完全二叉树的性质 N1要么为1要么为0,因为节点数为整数,所以N1为1
N0 = n;
4.⼀个具有767个结点的完全⼆叉树,其叶⼦结点个数为(B)
A 383
B 384
C 385
D 386
2N0 + N1 - 1 = 767 因为节点数为整数,所以N1为0
3.⼀棵完全⼆叉树的结点数位为531个,那么这棵树的⾼度为(B )
A 11
B 10
C 8
D 12