链式结构二叉树节点的定义
对链式结构二叉树进行操作,首先要创建二叉树节点,这里定义一个包含节点数据,左孩子节点,右孩子节点的结构体作为节点,之后的操作都使用这个结构体。代码如下:
typedef int BTDateType;
typedef struct BinaryTreeNode
{
BTDateType date;
struct BTNode*left;
struct BTNode*right;
}BTNode;
二叉树的遍历
学习二叉树的结构,最简单的办法就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树的节点进行相应的操作,并且每个节点都只操作一次。访问节点所作的操作取决于具体的应用问题。遍历是二叉树操作中最重要的操作,也是其它操作的基础。
按照规则,二叉树的遍历有:二叉树的前序/中序/后序的递归结构遍历。
1. 前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal):访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal):访问根结点的操作发生在遍历其左右子树之后。
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
前序遍历
前序遍历的遍历顺序是:根节点->左子树->右子树。递归进行。
前序遍历的递归图解:
代码如下:
void BinaryPrevOrder(BTNode*root)
{
if(root==NULL)
{
return;
}
printf("%d",root->date);
BinaryPrevOrder(root->left);
BinaryPrevOrder(root->right);
}
中序遍历
中序遍历的遍历顺序是:左子树->根节点->右子树。递归进行。
代码如下:
void BinaryInOrder(BTNode*root)
{
if(root==NULL)
{
return;
}
BinaryPrevOrder(root->left);
printf("%d",root->date);
BinaryPrevOrder(root->right);
}
后序遍历
后序遍历的遍历顺序是:左子树->右子树->根节点。递归进行。
代码如下:
void BinaryPostOrder(BTNode*root)
{
if(root==NULL)
{
return;
}
BinaryPrevOrder(root->left);
BinaryPrevOrder(root->right);
printf("%d",root->date);
}
层序遍历
除了前序遍历,中序遍历和后序遍历外,还可以对二叉树进行层序遍历。设二叉树根节点所在层为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层上的根节点,然后从左到右访问第二层的节点,接着是第三层的节点,以此类推,自上而下,自左至右的逐层访问二叉树,这种遍历方式称为层序遍历。
基本思路:根据层序遍历的特点,可以借助队列来实现逻辑。首先将根节点入队列,将根从队头出队时,将根的左右孩子依次从队尾入队,之后反复执行这一操作,直至所有元素都出队,二叉树遍历完毕。
代码如下:
//创建队列结构
typedef BTNode QDateType;
typedef struct QListNode
{
struct QNode* pnext;
QDateType date;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
//队列初始化
void QueueInit(Queue* q)
{
assert(q);
q->front = NULL;
q->rear = NULL;
}
//销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur != NULL)
{
QNode* next = cur->pnext;
free(cur);
cur = next;
}
q->front = q->rear = NULL;
}
//队尾插入数据
void QueuePush(Queue* q, QDateType date)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
newnode->date = date;
newnode->pnext = NULL;
if (q->front == NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->pnext = newnode;
q->rear = newnode;
}
}
//删除队头数据
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
QNode* next = q->front->pnext;
free(q->front);
q->front = next;
if (q->front = NULL)
{
q->rear = NULL;
}
}
//获取队头数据
QDateType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->date;
}
//判断队列是否为空
int QueueEmpty(Queue* q)
{
assert(q);
return q->front == NULL;
}
//层序遍历二叉树
void BinaryTreeLevelOrder(BTNode*root)
{
Queue q;
QueueInit(&q);
if(root!=Null)
{
QueuePush(&q,root);
}
while(!QueueEmpty(q))
{
BTNode*front= QueueFront(& q);
QueuePop(& q);
printf("%d ", front->data);
if(front->left)
{
QueuePush(&q,root->left);
}
if(front->right)
{
QueuePush(&q,root->right);
}
}
QueueDestroy(&q);
}
二叉树的深度遍历(接口型)
前面的几种遍历方式,是将一颗二叉树遍历并将遍历的元素打印出来。下面介绍的接口型遍历方式是将一颗二叉树遍历,并将遍历结果保存到一个动态开辟的数组中,遍历结束后返回这个数组。
思路:
1.首先计算二叉树中结点的个数,便于确定动态开辟的数组的大小。
2.遍历二叉树,将遍历结果存储到数组中。
3.返回数组
结点的个数
求解树的结点总数时:
1.树若为空,则结点个数为0。
2.树若不为空,则结点个数 = 左子树结点个数 + 右子树结点个数 + 1(自己)。
代码如下:
int BTNodeSize(BTNode*root)
{
return root==NULL?0:BTNodeSize(root->left)+BTNodeSize(root->right)+1 ;
}
前序遍历
代码如下:
int BTNodeSize(BTNode*root)
{
return root==NULL?0:BTNodeSize(root->left)+BTNodeSize(root->right)+1 ;
}
//将节点中的数据放入数组
void preorder(BTNode* root, int* arr, int* pi)
{
if (root == NULL)
return;
arr[(*pi)++] = root->data;
preorder(root->left, arr, pi);
preorder(root->right, arr, pi);
}
//遍历
int PrevOrderTraversal(BTNode*root,int*returnSize)
{
*returnSize=BTNodeSize(root);
int*arr=(int*)malloc(sizeof(int)*(*returnSize));
int i=0;
preorder( root, arr, &i);
return arr;
}
中序遍历
代码如下:
int BTNodeSize(BTNode*root)
{
return root==NULL?0:BTNodeSize(root->left)+BTNodeSize(root->right)+1 ;
}
//将节点中的数据放入数组
void Inorder(BTNode* root, int* arr, int* pi)
{
if (root == NULL)
return;
Inorder(root->left, arr, pi);
arr[(*pi)++] = root->data;
Inorder(root->right, arr, pi);
}
//遍历
int InOrderTraversal(BTNode*root,int*returnSize)
{
*returnSize=BTNodeSize(root);
int*arr=(int*)malloc(sizeof(int)*(*returnSize));
int i=0;
Inorder( root, arr, &i);
return arr;
}
后序遍历
代码如下:
int BTNodeSize(BTNode*root)
{
return root==NULL?0:BTNodeSize(root->left)+BTNodeSize(root->right)+1 ;
}
//将节点中的数据放入数组
void Postorder(BTNode* root, int* arr, int* pi)
{
if (root == NULL)
return;
Postorder(root->left, arr, pi);
Postorder(root->right, arr, pi);
arr[(*pi)++] = root->data;
}
//遍历
int PostOrderTraversal(BTNode*root,int*returnSize)
{
*returnSize=BTNodeSize(root);
int*arr=(int*)malloc(sizeof(int)*(*returnSize));
int i=0;
Postorder( root, arr, &i);
return arr;
}
二叉树的建立与销毁
建立二叉树
这里以前序为例建立二叉树,中序,后序及层序同理,不再赘述。
BTNode*CreatTree(int*arr,int*pi)
{
if(arr[*pi]==NULL)
{
(*pi)++;
return NULL;
}
BTNode*root=(BTNode*)malloc(sizeof(BTNode));
root->left=NULL;
root->right=NULL;
root->date=arr[*pi];
(*pi)++;
root->left=CreatTree(arr,pi);
root->right=CreatTree(arr,pi);
return root;
}
销毁二叉树
二叉树的销毁,与其他数据结构的销毁类似,都是一边遍历一边销毁。但是二叉树需要注意销毁结点的顺序,遍历时我们应该选用后序遍历,也就是说,销毁顺序应该为:左子树->右子树->根。
必须先将左右子树销毁,最后再销毁根结点,若先销毁根结点,那么其左右子树就无法找到,也就无法销毁了。
代码如下:
void BinaryTreeDestory(BTNode*root)
{
if(root==NULL)
{
return;
}
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}