二叉树: 任意一个节点的度都是小于等于 2 的.
节点定义
可以看到, 一个二叉树节点有三个部分:
1. 存储数据的位置
2. 指向左节点的指针
3. 指向右节点的指针
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data; // 用来存储当前节点的值
struct BinaryTreeNode* leftchild; // 指向左孩子
struct BinaryTreeNode* rightchild; // 指向右孩子
}BTNode;
三种遍历
前序遍历
当前节点: 都先访问自己数据, 然后访问自己的左右子树.
void Order(BTNode* root)
{
if(root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data); // 前序
Order(root->leftchild);
Order(root->rightchild);
}
中序遍历
当前节点: 在访问自己数据前, 要先确定自己的左子树已经先被访问了,
然后访问自己, 最后访问自己的右子树
void Order(BTNode* root)
{
if(root == NULL)
{
printf("N ");
return;
}
Order(root->leftchild);
printf("%d ", root->data); // 中序
Order(root->rightchild);
}
后序遍历
当前节点: 在访问自己数据时, 要确保自己的左子树已经被访问了,
然后自己的右子树也被访问了, 最后访问自己的数据
void Order(BTNode* root)
{
if(root == NULL)
{
printf("N ");
return;
}
Order(root->leftchild);
Order(root->rightchild);
printf("%d ", root->data); // 后序
}
总结
前序中序后序, 是站在根节点的角度来看的.
前序: 先访问根自己的数据, 再访问左右子树. 根的访问位于最前面
中序: 先访问自己的左子树, 然后访问自己的数据, 最后访问自己的右子树. 根的访问位于中间
后续: 先访问自己的右子树, 然后访问自己的右子树, 最后访问自己的数据. 根的访问位于最后
二叉树接口实现
创建节点
// 创建节点: 传入需要存储的值 x, 创建出节点并返回节点地址
BTNode* CreateNode(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
assert(newnode);
newnode->data = x;
newnode->leftchild = newnode->rightchild = NULL;
}
计算二叉树的节点个数
int BTreeSize(BTNOde* root)
{
if(root == NULL)
{
return 0;
}
return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
树节点个数 = 左子树的节点个数总和 + 右子树节点个数总和 + 1
这个 + 1 就是加上这个树的根
计算二叉树的叶子节点的个数
int BTreeLeafSize(BTNode* root)
{
if(root->NULL)
{
return 0;
}
if(root->leftchild == NULL && root->rightchild == NULL)
{
return 1;
}
return BTreeLeafSize(root->leftright) + BTreeLeafSize(root->rightchild);
}
叶子节点: 左右两个子树都是空
获得二叉树的高度
int BTreeHeight(BTNode* root)
{
if(root == NULL)
{
return 0;
}
left = BTreeHeight(root->leftchild);
right = BTreeHeight(root->rightchild);
return left > right ? left + 1 : right + 1;
}
return left > right ? left + 1 : right + 1;
// return BTreeHeight(root->leftchild)> BTreeHeight(root->rightchild)? BTreeHeight(root->leftchild)+ 1 : BTreeHeight(root->rightchild)+ 1;
return 语句不建议写成下面那种.
因为在执行完 BTreeHeight(root->leftchild)> BTreeHeight(root->rightchild), 之后因为没有保存结果, 就需要在执行一遍 BTreeHeight(root->leftchild) 和 BTreeHeight(root->rightchild), 这样会导致程序运行效率大大降低
获取二叉树第 k 层的节点个数
int BTreeLevelKSsize(BTNode* root, int k)
{
assert(k > 0);
if(root == NULL)
{
return 0;
}
if(k == 1)
{
return 1;
}
return BTreeLevelKSsize(root->leftchild, k - 1) + BTreeLevelKSsize(root->rightchild, k - 1);
}
判断二叉树中是否存在某个值
BTNode* BTreeFind(BTNode* root, BTDataType x)
{
if(root == NULL)
{
return NULL;
}
if(root->data == x)
{
return root;
}
BTNode* left = BTreeFind(root->leftchild, x);
if(left != NULL) // 当左子树中已经找到了这个值, 后续的查找就不需要了
{
return left;
}
BTNode* right = BTreeFind(root->rightchild, x);
if(right != NULL)
{
return right;
}
return NULL;
}
查找就是遍历, 通过遍历每一个节点, 并比较节点中的内容, 来判断某个值似乎否存在二叉树中
二叉树的销毁
void BTreeDestroy(BTNode* root)
{
if (root == NULL)
{
return;
}
Destroy(root->left);
Destroy(root->right);
root->left = root->right = NULL;
free(root);
}
想要删除就需要找到节点: 删除也是遍历, 通过遍历每个节点, 然后销毁这个节点.
一定要先执行两个 Destroy() 递归, 如果先执行 root->left = root->right = NULL;
那么传递给 Destroy() 的参数就是 NULL, 这样后面的节点就不会被销毁,
会照成内存泄漏.