二叉树链式结构的简单实现: 此处为了快速创建一棵二叉树,只是简单创建每一个节点然后把它们连接起来;
示例代码:
typedef int BTDataType;
//节点的结构体
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
//创建新的节点
BTNode* BuyNode(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
assert(newnode);
newnode->data = x;
newnode->left = NULL;
newnode->right = NULL;
return newnode;
}
//创建二叉树
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(4);
BTNode* node4 = BuyNode(3);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->left = node2;
node1->right = node3;
node2->left = node4;
node3->left = node5;
node3->right = node6;
return node1;
}
以上代码创建的二叉树如图:
所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
二叉树的前序遍历是先访问根节点,再访问左子树,最后访问右子树,即按照根 - 左子树 - 右子树的顺序遍历;示例代码:
//前序遍历
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return ;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
代码通过子问题,先打印根的数据,再将当前根的左子树进行递归,最后对右子树递归,结束条件为空,即完成了 根 - 左子树 - 右子树 的顺序访问,若以上面我们创建的二叉树为例,如图为运行结果,假设 N 为空:
遍历的过程我们可以分解为如下,假设 N 为空;
以下是以 1 为根的分解,左子树和右子树如下,
再往下分解如下:
所以遍历的顺序图如下,蓝色数字为访问节点的顺序:
先访问 1 的根节点,然后访问左子树,递归图解:
最后访问右子树,递归图解:
二叉树的中序遍历,与前序的区别是,中序遍历先访问左子树,再访问根,最后访问右子树,即按照左子树 - 根 - 右子树的顺序遍历;示例代码:
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
二叉树的后序遍历,与前序和中序相似,后序是先访问左子树,再访问右子树,最后访问根;按照左子树 - 右子树 - 根的顺序遍历;示例代码:
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
按如图顺序遍历为层序遍历:
层序遍历的思路是用队列实现,每打印出一个根的数据,就将这个根的左节点和右节点入队,如下图,使用上面我们创建的二叉树,再创建一个队列 q ,队列中存的是每个树的节点:
如果队列为空,根就进队列:
然后出队列,出队列后,根2和根4入队:
2出队,3和NULL入队:
我们使用以前实现的队列,将队列中每个节点的指针类型改为 BTNode* :
typedef struct BinaryTreeNode* QDataType;
//每个节点的结构体
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
//队列的结构体
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
层序遍历的示例代码:
//层序遍历
void LevelOrder(BTNode* root)
{
//用队列实现层序遍历
Queue q;
QueueInit(&q);
//节点不为空,就入队
if(root)
QueuePush(&q, root);
//每次出队之前,将队头的数据记录下来,然后出队,打印队头的值,再将出队的根的左右子树入队
while (!QIsEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if(front->left)
QueuePush(&q, front->left);
if(front->right)
QueuePush(&q, front->right);
}
printf("\n");
QueueDestroy(&q);
}
假设不打印空,运行的结果如下:
求二叉树节点个数化成子问题,左子树的节点个数加上右子树的节点个数加上自身节点;结束条件为空:
//二叉树节点个数
int BTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
求二叉树叶子节点个数化成子问题,将根的左子树的叶子数加上右子树的叶子数,结束条件为空或者根的左子树和右子树都为空,就是叶子,返回1;
//求叶子节点的个数
int BTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
求二叉树的高度化成子问题,取根的左子树和右子树中高度较高的那个,返回的是较高的那个再加一;结束条件为空;注意这里需要记录左子树和右子树的高度,不记录数据的话每次递归完会丢失数据,还要重新找数据,时间复杂度会翻呈指数形式的倍率;
int BTreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = BTreeHeight(root->left);
int rightHeight = BTreeHeight(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
求二叉树第k层结点个数化成子问题,求当前根的左子树的 k - 1 层加上右子树的 k - 1 层的节点个数;结束条件有两个,如果为空,返回0;如果 k == 1,表示就是第 k 层,返回1;
int BTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BTreeLevelKSize(root->left, k - 1) + BTreeLevelKSize(root->right, k - 1);
}
假设求第一层的节点,运行结果如下:
二叉树查找值为x的节点化成子问题,先判断当前的根是否是值为 x 的节点,若不是,就找在左子树中去找,在左子树中找不到,再去右子树找;结束条件为空或者找到值为x的节点;
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* r1 = BinaryTreeFind(root->left, x);
if (r1)
return r1;
BTNode* r2 = BinaryTreeFind(root->right, x);
if (r2)
return r2;
return NULL;
}
判断二叉树是否是完全二叉树,思路是用层序遍历的思维,完全二叉树是层序遍历连续的结果,如果遇到空后,队列中的数据还有有效的数据,说明不是完全二叉树;如果是完全二叉树,队列中应该全部是空;
//判断二叉树是否是完全二叉树
bool BTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
//出数据,遇到空就停止出
while (!QIsEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
break;
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
//判断队列里面是否还有数据,如果还有数据说明不是完全二叉树
//完全二叉树是层序遍历连续的结果
while (!QIsEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return false;
}
QueueDestroy(&q);
return true;
}
}
假设false为0,true为1,运行结果为:
二叉树销毁是按照后序的顺序,先销毁左子树,再销毁右子树,最后销毁根;
// 二叉树销毁
void BTreeDestory(BTNode* root)
{
if (root == NULL)
return;
BTreeDestory(root->left);
BTreeDestory(root->right);
free(root);
}