前言:本文讲解通过递归的方式实现二叉树的一些基本接口。
目录
通过左右子树的方式实现二叉树:
二叉树的遍历:
求二叉树结点的个数:
二叉树所有节点的个数:
二叉树叶子节点的个数:
求第k层节点的节点的个数 :
找二叉树中值为x的节点
求二叉树的高度:
构建二叉树:
销毁二叉树:
重头戏:中序遍历
判断二叉树是否是完全二叉树
通过左右子树的方式实现二叉树:
结构体的定义:
typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType val;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;
二叉树的遍历:
前序:根 ,左子树, 右子树
中序:左子树 ,根 ,右子树
后序: 左子树, 右子树, 根
根据上图分别走一遍前中后序:
前序:A ,B,NULL,NULL,C,NULL,NULL
中序:NULL,B,NULL,A,NULL,C,NULL
后序:NULL,NULL,B,NULL,NULL,C,A
想要理解前中后序需要了解递归
每一棵树都可以看成 根 左子树 右子树三部分组成。
代码实现:
//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("null ");return;}printf("%c ", root->val);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}
深入理解代码:
以下是上面代码的函数递归调用的代码走读情况。
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("null ");return;}BinaryTreeInOrder(root->left);printf("%d ", root->val);BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("null ");return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);printf("%d ", root->val);
}
求二叉树结点的个数:
二叉树所有节点的个数:
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
将问题分解:
如果 root为NULL,返回0;
每个二叉树结点的个数等于左子树节点的个数+右子树节点的个数+根(1);
代码深入理解:
二叉树叶子节点的个数:
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return BinaryTreeLeafSize(root->left)+BinaryTreeLeafSize(root->right);
}
将问题分解:
如果root为NULL,返回0;
如果左右子树为NULL,该节点为叶子节点就返回1;
二叉树的叶子节点的个数等于左子树叶子节点的个数+右子树叶子节点的个数。
求第k层节点的节点的个数 :
代码:
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root,int k)
{if (root == NULL)return 0;if (k==1)//此时root不等于空return 1;return BinaryTreeLevelKSize(root->left,k-1) + BinaryTreeLevelKSize(root->right,k-1);
}
分解问题:
将问题可以看成: 第K层节点的个数 = 左子树第K-1层节点的个数+右子树K-1层节点的个数
如果root为NULL,返回0;
如果层数为1且该root不为NULL时,返回1
找二叉树中值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->val == x){return root;}BTNode* ret1 = BinaryTreeFind(root->left, x);if (ret1)return ret1;BTNode* ret2 = BinaryTreeFind(root->right, x);if (ret2)return ret2;return NULL;
}
注意:要创建一个变量接受返回值,然后再返回,如果直接用函数返回返回值,那么就会出现多次重复调用.
分解问题:
找整棵树的问题转化为,找左子树和找右子树的问题
如果root为NULL,返回NULL
如果找到root->val==x,直接返回该节点的地址,
如果左右子树都没有x这个值ULL.
求二叉树的高度:
代码:
//求二叉树的高度
int BinaryTreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftheight = BinaryTreeHeight(root->left);int rightheight = BinaryTreeHeight(root->right);return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}
问题分解:
二叉树的高度 = 左右子树中高度最高的高度+1(根);
构建二叉树:
// 通过前序遍历的数组"ABC###D##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType*arr,int* pi)
{if (arr[*pi] == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));if (root == NULL){perror("malloc fail");return NULL;}root->left = NULL;root->right = NULL;root->val = arr[(*pi)++];root->left = BinaryTreeCreate(arr, pi);root->right = BinaryTreeCreate(arr, pi);return root;
}
‘#’是NULL
疑问:数组下标的指针pi,如果传值的话,就是临时拷贝,
如果传的是指针的话,因为是递归,调用递归的被调用的函数中i的改变不会改变调用该函数中的i的值,因为是值拷贝,如果还不理解的话,可以看一下关于函数值调用和址调用的区别。
销毁二叉树:
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);
}
二叉树的销毁采用后续遍历,如果采用前序或后序会非法访问,并且释放不干净。
重头戏:中序遍历
什么是中序遍历?
就是一层一层的遍历。
上图中序遍历的结果是: 1,2,3,4
那么如何实现中序遍历呢?
可以通过队列来实现,现将根节点入队列
每个元素出队列时,打印该节点的值,并将该元素的左右子树的根节点入队列,如果节点为NULL,那么就不入队列,直至队列为空为止。
这有一副流程图:
代码:
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//初始化队列if(root)QueuePush(&q,root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);//获得队头数据QueuePop(&q);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);printf("%c ", front->val);}
}
注意:这里队列中的存储的元素的数据类型为,二叉树节点的指针。
完成这个代码需要用到以前写过的队列的代码:
如果有需要这里有链接,数据结构: 实现初阶数据结构的代码 (gitee.com),如果不会队列的话,也可以看我以前的博客写的队列。
typedef BTNode* QDataType;
typedef struct QNode
{struct QNode* next;QDataType val;
}QNode;
判断二叉树是否是完全二叉树
这个思路跟上面中序遍历的思路一样,都需要用到队列,不同的是,中序遍历不将NULL入队列,
而这个如果左右子树的根为NULL,仍入队列。
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue 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)return false;}return true;
}
思路: 第一次出队列的元素为NULL时,就一直将队列中的元素一直出队列,如果全为NULL则为完全二叉树,只要有一个不为NULL,那么就不是完全二叉树。
结语:这就是今天的分享,希望看到这篇博客的人都能有所收获。