二叉树的基本概念
- 什么是二叉树
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的二叉树组成
- 特点
每个结点最多有两棵子树,即二叉树不存在度大于2的结点
二叉树的子树有左右之分,其子树的次序不能颠倒
- 两种特殊的二叉树
- 满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层上
- 完全二叉树:如果一棵具有N个结点的二叉树的结构与满二叉树的前N个结点的结构相同,称为完全二叉树
二叉树的性质
若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有(i>0)个结点
若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是(k>=0)
对任何一棵二叉树, 如果其叶结点个数为 n0,度为2的非叶结点个数为 n2,则有n0=n2+1
具有n个结点的完全二叉树的深度k为
log(n+1)
上取整对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
- 若i>0,双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲结点
- 若2i+1<n,左孩子序号:2i+1,否则无左孩子
- 若2i+2<n,右孩子序号:2i+2,否则无右孩子
-
二叉树的基本操作
-
二叉树的存储结构
typedef int TDataType;
typedef struct BTreeNode
{
TDataType data;
struct BTreeNode* left;
struct BTreeNode* right;
} BTreeNode;
//创建节点
BTreeNode* CreateNode(TDataType data)
{
BTreeNode* newNode = (BTreeNode*)malloc(sizeof(BTreeNode));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
- 创建一个二叉树
BTreeNode * CreateTree(int preOrder[], int size, int *pUsedSize)
{
int leftUse, rightUse;
int rootValue = preOrder[0];
//元素已经没有剩余了
if(size <= 0)
{
*pUsedSize = 0;
return NULL;
}
//空节点
if (rootValue == -1)
{
*pUsedSize = 1;
return NULL;
}
BTreeNode* root = CreateNode(rootValue);
root->left = CreateTree(preOrder + 1, size - 1, &leftUse);
root->right = CreateTree(preOrder + 1 + leftUse, size - 1 - leftUse, &rightUse);
//实际的使用情况
*pUsedSize = 1 + leftUse + rightUse;
return root;
}
- 前序、中序、后序的遍历(递归)
//前序遍历
void PreOrder(BTreeNode* root)
{
if (root == NULL)
{
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
//中序遍历
void InOrder(BTreeNode *root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTreeNode *root)
{
if (root == NULL)
{
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
- 前序、中序、后序遍历(非递归)
/*
非递归的方式我们需要用到栈的知识,
但是因为我们压栈,栈里面保存的是节点地址,
所以要对之前的栈的操作进行修改
struct BTreeNode;
//将原本的int类型改成BTreeNode*
typedef struct BTreeNode* StackDataType;
*/
#include "Stack.h"
void PreOrderLoop(BTreeNode *root)
{
Stack stack;
StackInit(&stack);
BTreeNode *cur;
cur = root;
BTreeNode *top;
if (root == NULL)
{
return;
}
while (cur != NULL || !StackEmpty(&stack))
{
while (cur != NULL)
{
printf("%d ", cur->data);
StackPush(&stack, cur);
cur = cur->left;
}
top = StackTop(&stack);
StackPop(&stack);
cur = top->right;
}
StackDestroy(&stack);
}
void InOrderLoop(BTreeNode *root)
{
Stack stack;
StackInit(&stack);
BTreeNode *cur = root;
BTreeNode *top;
if (root == NULL)
{
return;
}
while (cur != NULL || !StackEmpty(&stack))
{
while (cur != NULL)
{
StackPush(&stack, cur);
cur = cur->left;
}
top = StackTop(&stack);
StackPop(&stack);
printf("%d ", top->data);
cur = top->right;
}
StackDestroy(&stack);
}
void PostOrderLoop(BTreeNode *root)
{
Stack stack;
StackInit(&stack);
BTreeNode *cur = root;
BTreeNode *top;
BTreeNode *last = NULL;
while (cur != NULL || !StackEmpty(&stack))
{
while (cur != NULL)
{
StackPush(&stack, cur);
cur = cur->left;
}
top = StackTop(&stack);
if(top->right == NULL || top->right == last)
{
StackPop(&stack);
printf("%d ", top->data);
last = top;
}
else
{
cur = top->right;
}
}
StackDestroy(&stack);
}
- 层序遍历
/*
层序遍历属于广度优先,而队列具有这一性质,
所以我们用队列来实现层序遍历,
因为入队列我们放的是节点地址,
所以要对队列的操作进行修改
struct BTreeNode;
typedef struct BTreeNode * QDataType;
*/
void LevelOrder(BTreeNode *root)
{
Queue queue;
QueueInit(&queue);
BTreeNode *front;
if (root == NULL)
{
return;
}
QueuePush(&queue, root);//放的是节点地址,不是data
while (!QueueEmpty(&queue))
{
front = QueueFront(&queue);
QueuePop(&queue);
if (front->left != NULL)
{
QueuePush(&queue, front->left);
}
if (front->right != NULL)
{
QueuePush(&queue, front->right);
}
printf("%d ", front->data);
}
QueueDestroy(&queue);
}