关于二叉树的相关操作,是在先要构造出一个二叉树的基础之上,如何创建二叉树?树的存储结构又是怎样表示的?
树的存储结构
typedef int DataType1;
typedef struct TNode
{
DataType1 data;
struct TNode *lctree;
struct TNode *rctree;
}TNode;
二叉树的创建
- 根据带空结点的前序,采用递归的形式创建二叉树
- 将函数外的前序数组size以传址的方式带入CreateTree函数内部,每创建一个结点都在函数内部使size-1
- 局部视野下,每当创建完左子树,需要创建右子树时都可以通过未创建结点前的数组大小,减去创建根结点以及左子树后的数组大小
//创建结点
TNode* CreateTNode(DataType1 data)
{
TNode *newNode = (TNode *)malloc(sizeof(TNode));
assert(newNode);
newNode->data = data;
newNode->lctree = NULL;
newNode->rctree = NULL;
return newNode;
}
TNode *CreateTree(DataType1 *preorder, int *sz)
{
TNode *root = NULL;
TNode *lroot = NULL;
TNode *rroot = NULL;
int n = *sz;//局部视野下未创建结点前的数组大小
DataType1 rootValue = 0;
if (*sz == 0)
{
return NULL;
}
rootValue = preorder[0];
(*sz)--;
if (rootValue == -1)
{
return NULL;
}
root = CreateTNode(rootValue);
lroot = CreateTree(preorder+1, sz);
root->lctree = lroot;
rroot = CreateTree(preorder+(n-(*sz)), sz);//n-(*sz)表示用去的结点个数
root->rctree = rroot;
return root;
}
二叉树的前/中/后序遍历
相对于左右子树的遍历顺序,根结点的遍历次序的不同形成了二叉树的不同遍历形式,根—左子树—右子树的顺序是前序遍历;左子树—根---右子树的顺序是中序遍历;左子树—右子树—根的顺序是后序遍历
void Preorder(TNode *root)
{
if (root == NULL)
{
return;
}
printf("%d ", root->data);
Preorder(root->lctree);
Preorder(root->rctree);
}
void Inorder(TNode *root)
{
if (root == NULL)
{
return;
}
Inorder(root->lctree);
printf("%d ", root->data);
Inorder(root->rctree);
}
void Postorder(TNode *root)
{
if (root == NULL)
{
return;
}
Postorder(root->lctree);
Postorder(root->rctree);
printf("%d ", root->data);
}
求节点的个数
递推式:结点的个数=左子树节点个数+右子树节点个数+1(根节点)
二叉树有五种形式:空树、仅有一个结点、有左子树、有右子树、左右子树都存在
仅当空树时返回0,其余情况均满足递推式
int GetNodeSize(TNode *root)
{
if (root == NULL)
{
return 0;
}
return GetNodeSize(root->lctree) + GetNodeSize(root->rctree) + 1;
}
求第K层结点个数
递推式:第k层结点个数=左子树k-1层结点个数+右子树k-1层结点个数
假设根结点为第1层,求第k层即求左右子树的第k-1层节点个数之和,如果有第k层,则不断递归,当k==1时就为第k层,此时返回1,无法递归到第k层遇到空树返回0
int LevelKNode(TNode *root, int k)
{
int left = 0;
int right = 0;
if (root == NULL)
{
return 0;
}
if (k==1)
{
return 1;
}
left = LevelKNode(root->lctree, k-1);
right = LevelKNode(root->rctree, k-1);
return left + right;
}
叶子结点个数
递推式:叶子结点个数=左子树叶子结点个数+右子树叶子结点个数
空树时返回0;左右子树都为空树时,表示该结点为叶子结点,返回1
int GetLeafSize(TNode *root)
{
if(root == NULL)
{
return 0;
}
if (root->lctree == NULL && root->rctree == NULL)
{
return 1;
}
return GetLeafSize(root->lctree) + GetLeafSize(root->rctree);
}
求树的高度
递推式:MAX(左子树高度, 右子树高度)+ 1
空树返回0,其余情况均满足上面递推式
#define MAX(a,b) ((a) > (b) ? (a) : (b))
int GetHeight(TNode *root)
{
int LeftHeight = 0;
int RightHeight = 0;
if (root == NULL)
{
return 0;
}
LeftHeight = GetHeight(root->lctree);
RightHeight = GetHeight(root->rctree);
return MAX(LeftHeight, RightHeight) + 1;
}
在树中查找结点
找到了返回结点地址,找不到返回NULL
先判断根结点,如果根命中返回根结点地址,若未命中分别到左右子树里面去递归查找
TNode *Search(TNode *root, DataType1 data)
{
TNode *node = NULL;
if (root == NULL)
{
return NULL;
}
if (root->data == data)
{
return root;
}
node = Search(root->lctree, data);
if (node != NULL)
{
return node;
}
node = Search(root->rctree, data);
if (node != NULL)
{
return node;
}
return NULL;
}
层序遍历
借助队列,根据队列先进先出的特点,先让根节点入队列,再分别让根结点的左右子树(如果不为空树)的根结点入队列,每次都取队首元素遍历,遍历完后出队列,当队列不为空时持续上面的过程,直到遍历完成
void LevelOrder(TNode *root)
{
TNode *front = NULL;
Queue q;
QueueInit(&q);
if (root == NULL)
{
return;
}
InQueue(&q, root);
while (!QueueEmpty(&q))
{
front = (TNode *)QueueFront(&q);
printf("%d ", (front->data));
if (front->lctree != NULL)
{
InQueue(&q, front->lctree);
}
if (front->rctree != NULL)
{
InQueue(&q, front->rctree);
}
OutQueue(&q);
}
QueueDestroy(&q);
}
判断是否为完全二叉树
借助带空结点的层序遍历完成,如果层序遍历的过程中遇到空结点,之后仍存在不为空结点的结点,说明该树不为完全二叉树,否则就是完全二叉树
int IsCompleteBTree(TNode *root)
{
TNode *front = NULL;
Queue q;
QueueInit(&q);
if (root == NULL)
{
return 1;
}
InQueue(&q, root);
while (!QueueEmpty(&q))
{
front = (TNode *)QueueFront(&q);
if (front == NULL)
{
break;
}
//无需判断,空结点同样入队列
InQueue(&q, front->lctree);
InQueue(&q, front->rctree);
OutQueue(&q);
}
while (!QueueEmpty(&q))
{
front = (TNode *)QueueFront(&q);
if (front != NULL)
{
QueueDestroy(&q);
return 0;
}
OutQueue(&q);
}
QueueDestroy(&q);
return 1;
}
前/中/后序的非递归遍历
递归遍历是依靠系统调用栈完成,非递归遍历则是依靠数据结构栈完成
在后序遍历的中,要对右子树是否遍历过进行判断
void PreorderLoop(TNode *root)
{
TNode *node = NULL;
Stack stack;
StackInit(&stack);
node = root;
while (node != NULL || !StackEmpty(&stack))
{
while (node != NULL)
{
printf("%d ", node->data);
StackPush(&stack, node);
node = node->lctree;
}
node = ((TNode *)StackTop(&stack))->rctree;
StackPop(&stack);
}
StackDestroy(&stack);
}
void InorderLoop(TNode *root)
{
TNode *node = NULL;
Stack stack;
StackInit(&stack);
node = root;
while (node != NULL || !StackEmpty(&stack))
{
while (node != NULL)
{
StackPush(&stack, node);
node = node->lctree;
}
printf("%d ", ((TNode *)StackTop(&stack))->data);
node = ((TNode *)StackTop(&stack))->rctree;
StackPop(&stack);
}
StackDestroy(&stack);
}
void PostorderLoop(TNode *root)
{
TNode *node = NULL;
TNode *top = NULL;
TNode *last = NULL;
Stack stack;
StackInit(&stack);
node = root;
while (node != NULL || !StackEmpty(&stack))
{
while (node != NULL)
{
StackPush(&stack, node);
node = node->lctree;
}
top = (TNode *)StackTop(&stack);
if (top->rctree == NULL || top->rctree == last)//top->rctree == last时表示右子树已遍历
{
printf("%d ", top->data);
last = top;
StackPop(&stack);
}
else
{
node = top->rctree;
}
}
StackDestroy(&stack);
}
镜像二叉树
当为空树或者为叶子结点时直接返回,其余情况均交换左右子树,整个过程递归实现
void Mirror(TNode *root)
{
TNode *tmp = NULL;
if (root == NULL)
{
return;
}
if (root->lctree != NULL && root->rctree != NULL)
{
tmp = root->lctree;
root->lctree = root->rctree;
root->rctree = tmp;
Mirror(root->lctree);
Mirror(root->rctree);
}
}
找两结点的最近祖先结点
整个过程递归完成,如果两结点分别在根节点的左右子树中,则说明此根结点为最近的祖先结点,如果两个结点均在同一子树中,则递归进入该子树进行查找
TNode * GetNearestAncestor(TNode *root, DataType1 d1, DataType1 d2)
{
TNode *d1InLeft = Search(root->lctree, d1);
TNode *d2InLeft = Search(root->lctree, d2);
if (d1InLeft && !d2InLeft) {
return root;
}
if (!d1InLeft && d2InLeft) {
return root;
}
if (d1InLeft && d2InLeft) {
return GetNearestAncestor(root->lctree, d1, d2);
}
return GetNearestAncestor(root->rctree, d1, d2);
}
判断是否为平衡二叉树
什么样的树是平衡二叉树?
- 空树是平衡二叉树
- 平衡二叉树的左右子树也为平衡二叉树
- 平衡二叉树的左右子树高度差不超过1
先递归判断左右子树是否为平衡二叉树,如果都是再判断左右子树的高度差
int IsBalance(TNode *root)
{
int leftHeight, rightHeight, diff, isBalance;
if (root == NULL) {
return 1;
}
isBalance = IsBalance(root->lctree);
if (isBalance == 0) {
return 0;
}
isBalance = IsBalance(root->rctree);
if (isBalance == 0) {
return 0;
}
leftHeight = GetHeight(root->lctree);
rightHeight = GetHeight(root->rctree);
diff = leftHeight - rightHeight;
if (diff < -1 || diff > 1) {
return 0;
}
else {
return 1;
}
}
求树最远的结点距离
空树的最远结点距离为0,剩下的可分为两种情况:最远距离=左子树高度+右子树高度,另一种情况即为最远距离诞生于左/右子树,此时递归求取该子树的最远距离,然后取其较大者即为最终要求的最远距离
int Max(int a, int b, int c)
{
int max = 0;
if (a >= b)
{
max = a;
}
if (c >= max)
{
max = c;
}
return max;
}
int GetFarrestDistance(TNode *root)
{
int leftDistance, rightDistance, leftHeight, rightHeight, rootDistance;
if (root == NULL)
{
return 0;
}
leftDistance = GetFarrestDistance(root->lctree);
rightDistance = GetFarrestDistance(root->rctree);
leftHeight = GetHeight(root->lctree);
rightHeight = GetHeight(root->rctree);
rootDistance = leftHeight + rightHeight;
return Max(leftDistance, rightDistance, rootDistance);
}
根据前序和中序创建二叉树
根据前序和中序能够确定二叉树的根节点、左子树、右子树三部分,然后递归的创建每一个子树
TNode * CreateTree2(DataType1 preorder[], DataType1 inorder[], int size)
{
int i, r;
DataType1 rootValue = preorder[0];
TNode *root = NULL;
if (size <= 0)
{
return NULL;
}
for (i = 0; i < size; i++)
{
if (inorder[i] == rootValue)
{
r = i;
break;
}
}
assert(r < size);
root = CreateTNode(rootValue);
root->lctree = CreateTree2(preorder + 1, inorder, r);
root->rctree = CreateTree2(preorder + 1 + r, inorder + r + 1, size - 1 - r);
return root;
}
同样根据后序和中序也能够创建二叉树
TNode * CreateTree3(DataType1 inorder[], DataType1 postorder[], int size)
{
int i, r;
DataType1 rootValue = postorder[size - 1];
TNode *root = NULL;
if (size <= 0)
{
return NULL;
}
for (i = 0; i < size; i++)
{
if (inorder[i] == rootValue)
{
r = i;
break;
}
}
assert(r < size);
root = CreateTNode(rootValue);
root->lctree = CreateTree3(inorder, postorder, r);
root->rctree = CreateTree3(inorder + 1 + r, postorder + r, size - 1 - r);
return root;
}