二叉树的链式结构
二叉树是一种特殊的树形结构,由根节点加上左右两棵子树组成,每个节点最多有两棵子树,不存在度大于2的节点。没有节点存在时也称为空二叉树。
二叉树不适合顺序存储,顺序结构只包含有效元素,没法确定哪个节点有还是没有左右孩子这层关系。链式结构更适合建立二叉树。
二叉树有双亲表示法,孩子表示法,孩子兄弟表示法等,这里实现用的是孩子表示法,即节点中表示出与其孩子的关系
创建一个二叉树节点的结构,left指针指向左孩子,right指向右孩子
typedef int BDatatype;
typedef struct BTNode {
BDatatype data;
struct BTNode* left;
struct BTNode* right;
}BTNode;
给到一个序列, -1表示空值,int array[] = { 1,2,3,-1,-1,-1,4,5,-1,-1,6};
二叉树的创建,
首先给出创建一个节点的方法
然后用index从数组第0号元素开始,遇到不是空值的数据就为该值创建一个节点,先创建的是根节点,然后递归创建左子树右子树,这个顺序是先序的顺序。invalid表示传入的空值。
BTNode* BuyBinaryTreeNode(BDatatype data) {
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL) {
assert(0);
return NULL;
}
node->data = data;
node->left = node->right = NULL;
return node;
}
BTNode* _CreateBinaryTree(BDatatype array[], int size, int* index,BDatatype invalid) {
BTNode* root = NULL;
if (*index < size && invalid != array[*index]) {
root = BuyBinaryTreeNode(array[*index]);
++(*index);
root->left = _CreateBinaryTree(array, size, index, invalid);
++(*index);
root->right = _CreateBinaryTree(array, size,index, invalid);
}
return root;
}
BTNode* CreateBinaryTree(BDatatype array[], int size, BDatatype invalid) {
int index = 0;
return _CreateBinaryTree(array, size, &index, invalid);
}
二叉树的先序遍历
从根节点开始,再遍历左子树再遍历右子树
void PreOrder(BTNode* root) {
if(root) {
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
}
中序遍历
左子树->根->右子树
void InOrder(BTNode* root) {
if (root) {
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
}
后序遍历
左子树->右子树->根
void PostOrder(BTNode* root) {
if (root) {
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
}
层序遍历
二叉树的层序遍历用到了队列的特性,先创建一个队列,从根节点开始入队列,用一个指针指向队头元素,如果该节点的左孩子存在,将左孩子入队列,接下来判断右孩子,如果右孩子存在,将右孩子也入队列,每次打印队头元素,然后将队头元素出队列再去找下一个。就实现了层序遍历。
void LeveOrder(BTNode* root) {
Queue q;
if (NULL == root) {
return;
}
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q)) {
BTNode* cur = Queuefront(&q);
printf("%d ", cur->data);
if (cur->left) {
QueuePush(&q, cur->left);
}
if (cur->right) {
QueuePush(&q, cur->right);
}
QueuePop(&q);
}
QueueDestroy(&q);
}
二叉树销毁
后序遍历的规则来删除可以保证不破坏二叉树的特性,所以后续遍历找到一个节点就释放一个。
void BinaryTreeDestroy(BTNode** root) {
if (NULL == *root) {
return;
}
BinaryTreeDestroy(&(*root)->left);
BinaryTreeDestroy(&(*root)->right);
free(root);
root = NULL;
}
求节点的个数
如果是空树则节点个数为0,不是的话根节点算一个,然后递归去找它的左子树和右子树,找到一个加一个。
int BinaryTreesize(BTNode* root) {
if (NULL == root) {
return 0;
}
return 1 + BinaryTreesize(root->left) + BinaryTreesize(root->right);
}
求叶子节点的个数
根据叶子节点的特性,它的左孩子和右孩子都不存在,直接递归找左子树和右子树中左孩子和右孩子都为空的节点。
int Binaerleafsize(BTNode* root) {
if (NULL == root) {
return 0;
}
//左右孩子都不存在,
if (NULL == root->left && NULL == root->right) {
return 1;
}
return Binaerleafsize(root->left) + Binaerleafsize(root->right);
}
求第K层的节点个数
根据二叉树的特性,一个节点最多只有左子树和右子树两个子树,一个二叉树有K层,它的至少一个子树有k-1层,所以递归去求左右子树中k-1层的节点个数,直到剩下根节点那一层就返回1。
int BinaryTreeLevelKSize(BTNode* root, int k) {
if (NULL == root || k <= 0) {
return 0;
}
if (k == 1) {
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) +
BinaryTreeLevelKSize(root->right, k - 1);
}
求数的高度,
递归求左子树高度,再递归求右子树高度, 哪个高,哪个就是二叉树的高度。
int BinaryTreeHeight(BTNode* root) {
if (NULL == root) {
return 0;
}
int hleft = BinaryTreeHeight(root->left)+1;
int hright = BinaryTreeHeight(root->right)+1;
if (hleft > hright) {
return hleft;
}
else {
return hright;
}
}
查找值为x的节点
先将这个值与根节点中的data值比较,存在就返回,不存在再递归去左子树中找,没有就递归去右子树中找。
BTNode* BinaryTreeFind(BTNode* root, BDatatype x) {
if (NULL == root) {
return NULL;
}
if (x == root->data) {
return root;
}
int ret = BinaryTreeFind(root->left, x);
if (ret) {
return ret;
}
else {
return BinaryTreeFind(root->right, x);
}
}
判断是不是完全二叉树
思路是找到树中第一个不饱和的节点,要满足完全二叉树的特性,那从该节点往后所有的节点都不能有孩子。借助队列的特性来实现,首先将根节点入队列,定义一个cur取到队头元素,如果左右孩子都存在,将它的左右孩子分别入队列,再删除队头元素,此时cur就指向队列中下一个元素,再进行判断,如果只有左孩子没有右孩子,那这个节点就是最后一个不饱和的节点,将标记置为1,进入判断语句中,如果这个节点之后的节点有一个孩子存在,那就不是完全二叉树。
int BinaryTreeComplete(BTNode* root) {
//空树也是完全二叉树
if (root == NULL) {
return 1;
}
//层序检测检测 空树也是完全二叉树
Queue q;
int flag = 0;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q)) {
BTNode* cur = Queuefront(&q);
if (flag) {
if (cur->left || cur->right) {
QueueDestroy(&q);
return 0;
}
}
else {
if (cur->left && cur->right) {
QueuePush(&q, cur->left);
QueuePush(&q, cur->right);
}
//左孩子不空 右空
else if (cur->left) {
QueuePush(&q, cur->left);
flag = 1;
}
else if (cur->right) {
QueueDestroy(&q);
return 0;
}
else {
flag = 1;
}
}
}
QueuePop(&q);
return 1;
}