目录
二叉树
首先,我们先了解一下树的概念:
树
树的概念
树,其实是由有限个数构成的一个具有层次关系的集合,它是一种非线性的数据结构。
我们之所以叫它树,是因为它像倒挂着的树,由根向下递归展开。
树的相关概念
节点:相当于这个树中每一个数字,而最上面那个节点叫根节点,由根节点延申下来的数叫子节点或者叫子树。
节点的度:一个节点含有的子树的个数称为该节点的度,例如上图中1的度为2,2的度为2,4的度为0。
叶节点或终端节点:度为0的节点称为叶节点,例如上图中的4,5,6,7都是叶节点。
非终端节点或分支节点:度不为0的节点,例如上图中2和3。
双亲结点或父节点:如果一个节点含有子节点,则这个节点称为其子节点的双亲结点,如上图1是2的父节点,也是3的父节点。
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点,如上图2是1的子节点。
兄弟节点:具有相同父节点的子节点互称为兄弟节点,如上图中2,3都是1的子节点,2和3互为兄弟节点。
树的度:一棵树中,节点度数最大的那个就是树的度,上图树的度为2。
节点的层次:由根节点开始定义,根节点为第一层,根的子节点为第二层,以此类推。
树的高度或深度:树中节点最大的层次,上图树的高度为3。
堂兄弟节点:双亲在同一层的节点互为堂兄弟,上图中5和6就互为堂兄弟节点。
节点的祖先:从根到该节点所经分支上的所有节点,上图1就是所有节点的祖先。
子孙:以某节点为根的子树中任一节点都称为该节点的子孙,上图所有节点都是1的子孙。
森林:这些互不相交的树的集合称为森林。
注意事项:
子树之间不能有交集,子树之间不能相交
除了根节点外,每个节点有且仅有一个父节点
二叉树的概念
简单来说,二叉树就是树的度数为2的。
任何一个二叉树都是由以下几种情况复合而成:
特殊的二叉树
满二叉树:一个二叉树,如果每一层的节点数都是最大值,那么这个二叉树就是满二叉树。假设这个满二叉树有x层,那么利用等比公式,可以算出它的节点总数就是2x -1个。
完全二叉树:简单来说就是一个二叉树按照层序遍历(层序遍历后面会说)从左到右都是连续的,如果一个子树,它没有左子树,但是有右子树,那么它就不是完全二叉树。满二叉树是一种特殊的完全二叉树。
二叉树的性质
1、若根节点的层数为1,则一个非空的二叉树它第x层最多有2x-1个节点,如果深度为h的二叉树,它的节点总数不超过2x -1。
2、对于满二叉树而言,若根节点的层数为1,如果总节点数为n,那么高度h=log2(n+1)。
3、对任何一个二叉树,设度为0的子树有n0个,度为2的有n2个,则n2+1=n0。
二叉树的存储结构
顺序存储
也就是二叉树用层序遍历在数组中依次存储,但是考虑到数据存储问题,一般是存储完全二叉树的数据才用数组的形式存储,因为非完全二叉树可能会有左子树为空,右子树有数据的问题导致数组有些位置空缺,造成空间的浪费。所以一般使用数组存储适合完全二叉树。
在数组存储中我们发现:假设一个父节点在数组中的下标为i,并且有左右子树的情况下(左右子树都不为空),那么它的左子树下标为2i+1,右子树下标为2i+2,如果2i+1>=n(n为总结点数),那么该节点没有左子树,同理,如果2i+2>=n,则该节点没有右子树。
链式存储
用链表来表示一个二叉树,用结构体创建3个结构体变量,分别表示当前节点的数据和左右指针。
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* Left; // 指向当前节点左孩子
struct BinTreeNode* Right; // 指向当前节点右孩子
BTDataType data; // 当前节点值域
}
二叉树的遍历
前序遍历
前序遍历就是先遍历根节点,再遍历左子树,再遍历右子树。
我们来试试这道题:二叉树的前序遍历
void Traversal(struct TreeNode* root, int* node, int* returnSize)
{
if(root == NULL)
{
return;
}
node[(*returnSize)++] = root->val;
Traversal(root->left,node,returnSize);
Traversal(root->right,node,returnSize);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
int* node = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
Traversal(root, node,returnSize);
return node;
}
中序遍历
中序遍历就是先遍历左子树,再遍历根节点,再遍历右子树。
试题:二叉树的中序遍历
void Traversal(struct TreeNode* root,int* returnSize,int* arr)
{
if(root == NULL)
{
return;
}
Traversal(root->left,returnSize,arr);
arr[(*returnSize)++] = root->val;
Traversal(root->right,returnSize,arr);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
int* arr = (int*)malloc(sizeof(int)*100);
*returnSize = 0;
Traversal(root,returnSize,arr);
return arr;
}
后序遍历
后序遍历就是先遍历左子树,再遍历右子树,再遍历根节点。
试题:二叉树的后序遍历
void preorder(int* arr,int* returnSize, struct TreeNode* root)
{
if(root == NULL)
{
return;
}
preorder(arr,returnSize,root->left);
preorder(arr,returnSize,root->right);
arr[(*returnSize)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
int* arr = (int*)malloc(sizeof(int) * 200);
*returnSize = 0;
preorder(arr,returnSize,root);
return arr;
}
如果容易忘记,你就这么理解:把左右子树看成一个整体,把根节点看成要插队的人
前序遍历就是插在前面,变成根,左子树,右子树
中序遍历就是插在中间,变成左子树,根,右子树
后序遍历就是插在后面,变成左子树,右子树,根。
层序遍历
顾名思义,就是一层一层遍历。
关于二叉树的函数接口
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
二叉树节点个数:
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;//进入if语句,表示是空节点
}
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;//走到这里表示不是空节点,再判断左右子树是不是空节点,再+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);
}
二叉树第k层节点个数:
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1 && root != NULL)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
二叉树查找值为x的节点:
BTNode* BinaryTreeFind(BTNode* root, BTdatatype x)
{
if (root == NULL)
{
return 0;
}
if (root->data == x)
{
return root;
}
BTNode* ret1 = BinaryTreeFind(root->left, x);
if (ret1)
{
return ret1;
}
BTNode* ret2 = BinaryTreeFind(root->right, x);
if(ret2)
{
return ret2;
}
}
二叉树的基础oj练习
单值二叉树
传送门:点击这里
bool isUnivalTree(struct TreeNode* root){
if(root == NULL)
{
return true;
}
if(root->left != NULL && root->left->val != root->val)
{
return false;
}
if(root->right != NULL && root->right->val != root->val)
{
return false;
}
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
相同的树
传送门:点击这里
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 false;
}
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
对称二叉树
传送门:点击这里
bool Symmetric(struct TreeNode* left, struct TreeNode* right)
{
if(left == NULL && right == NULL)
{
return true;
}
if(left == NULL || right == NULL)
{
return false;
}
if(left->val != right->val)
{
return false;
}
return Symmetric(left->left,right->right) && Symmetric(left->right,right->left);
}
bool isSymmetric(struct TreeNode* root){
if(root == NULL)
{
return true;
}
return Symmetric(root->left,root->right);
}
另一棵树的子树
传送门:点击这里
bool Subtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root == NULL && subRoot == NULL)
{
return true;
}
if(root == NULL || subRoot == NULL)
{
return false;
}
if(root->val != subRoot->val)
{
return false;
}
if(root->left && subRoot->left)
{
if(root->left->val != subRoot->left->val)
{
return false;
}
}
if(root->right && subRoot->right)
{
if(root->right->val != subRoot->right->val)
{
return false;
}
}
return Subtree(root->left,subRoot->left) && Subtree(root->right,subRoot->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL)
{
return false;
}
if(Subtree(root,subRoot))
{
return root;
}
else
{
return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}
}
今天的oj练习就练到这里,如果觉得这篇文对你有帮助的话,就点个赞吧,如有错误,望指出!