提起樹,大家肯定想到了平時生活中見到的樹,它由根、樹枝和樹葉組成。而我們這裏說的樹,其實就是把生活中的樹的模型給顛倒過來了。如下圖所示:
二叉樹是通過上述5種形式的組合或嵌套而形成。
二叉樹的概念:
一棵二叉樹其實是結點的一個有限集合,該集合要麼爲空,要麼由一個根結點再加上兩棵分別稱爲左子樹和右子樹的二叉樹組成。二叉樹的特點:
每個結點最多有兩棵子樹,及二叉樹不存在度大於2的結點
二叉樹的子樹有左右之分,其子樹順序不能顛倒
接下來,我們對二叉樹做以下基本操作:
頭文件聲明:
#pragma once
#include<stddef.h>
typedef char TreeNodeType;
typedef struct TreeNode
{
TreeNodeType data;
struct TreeNode* lchild;
struct TreeNode* rchild;
}TreeNode;
void TreeInit(TreeNode** pRoot);//初始化
void TreePreOrder(TreeNode* root);//先序遍歷
void TreeInOrder(TreeNode* root);//中序遍歷
void TreePostOrder(TreeNode* root);//後序遍歷
void TreeLevelOrder(TreeNode* root);// 層序遍歷
TreeNode* CloneTree(TreeNode* root);//克隆樹
void TreeDestroy(TreeNode* root);//銷燬樹
size_t TreeSize(TreeNode* root);//求樹的節點個數
size_t TreeLeafSize(TreeNode* root);//求葉子節點個數
size_t TreeKLevelSize(TreeNode* root,size_t k);//求第k層節點個數
size_t TreeHeight(TreeNode* root);//求樹的高度
TreeNode* TreeFind(TreeNode* root,TreeNodeType to_find);//在於一個樹中找某個值的位置
TreeNode* ParentTree(TreeNode* root,TreeNode* child);//找一個節點的父節點
TreeNode* LChildNode(TreeNode* root,TreeNode* lchild);//找一個節點的左孩子
TreeNode* RChildNode(TreeNode* root,TreeNode* rchild);//找一個節點的右節點
假如現在有一個這樣的二叉樹,我們該如何對它進行遍歷呢?
具體函數實現代碼如下:
#include"bin_tree.h"
#include"seqqueue.h"
#include<stdio.h>
#include<stdlib.h>
void TreeInit(TreeNode** pRoot)
{
if(pRoot == NULL)
{
return;//非法輸入
}
(*pRoot) = NULL;
}
TreeNode* CreateTreeNode(TreeNodeType value)//創建數的結點
{
TreeNode* new_node = (TreeNode*)malloc(sizeof(TreeNode));
new_node->data = value;
new_node->lchild = NULL;
new_node->rchild = NULL;
return new_node;
}
void DestroyTreeNode(TreeNode* node)//銷燬結點
{
free(node);
}
//前序遍歷樹
void TreePreOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空樹
}
//先訪問根節點
printf("%c ",root->data);
//然後遞歸的遍歷左子樹
TreePreOrder(root->lchild);
//最後遞歸的遍歷右子樹
TreePreOrder(root->rchild);
return;
}
//中序遍歷樹
void TreeInOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空樹
}
//先遞歸的訪問左子樹
TreeInOrder(root->lchild);
//然後訪問根節點
printf("%c ",root->data);
//最後遞歸的訪問右子樹
TreeInOrder(root->rchild);
return;
}
//後序遍歷樹
void TreePostOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空樹
}
//先遞歸訪問左子樹
TreePostOrder(root->lchild);
//再遞歸的訪問右子樹
TreePostOrder(root->rchild);
//最後訪問根節點
printf("%c ",root->data);
return;
}
//層序遍歷,藉助隊列來實現
//循環的取隊首元素,訪問並出棧,然後將其左右子樹入隊列
//循環上述過程,直到隊列爲空,說明訪問完了
void TreeLevelOrder(TreeNode* root)
{
if(root == NULL)
{
return;//空樹
}
SeqQueue queue;
SeqQueueInit(&queue);
SeqQueuePush(&queue,root);
while(1)
{
SeqQueueType front;
size_t ret = SeqQueueFront(&queue,&front);
if(ret == 0)
{
break;//隊列爲空
}
printf("%c ",front->data);
SeqQueuePop(&queue);
if(front->lchild != NULL)
{
SeqQueuePush(&queue,front->lchild);
}
if(front->rchild != NULL)
{
SeqQueuePush(&queue,front->rchild);
}
}
printf("\n");
return;
}
TreeNode* _CreateTree(TreeNodeType data[],size_t size,size_t* index,TreeNodeType null_node)//創建樹
{
if(index == NULL)
{
return NULL;//非法輸入
}
if(*index >= size)
{
return NULL;//指針越界
}
if(data[*index] == null_node)
{
return NULL;//子樹爲空
}
//根據index指向的內容,創建一個節點
TreeNode* root = CreateTreeNode(data[*index]);
//先++index,然後遞歸構建新節點的左子樹
++(*index);
root->lchild = _CreateTree(data,size,index,null_node);
//再++index,然後遞歸的構建新節點的右子樹·
++(*index);
root->rchild = _CreateTree(data,size,index,null_node);
return root;
}
//借用先序遍歷的規則和用#表示一個空子樹
TreeNode* CreateTree(TreeNodeType data[],size_t size,TreeNodeType null_node)
{
//每次指向的元素
size_t index = 0;
//藉助下面這個函數完成每次的遞歸調用
return _CreateTree(data,size,&index,null_node);
}
TreeNode* CloneTree(TreeNode* root)//克隆一棵樹
{
if(root == NULL)
{
return;//空樹
}
//按照先序方法來遍歷
TreeNode* new_node = CreateTreeNode(root->data);
new_node->lchild = CloneTree(root->lchild);
new_node->rchild = CloneTree(root->rchild);
return new_node;
}
void TreeDestroy(TreeNode* root)//銷燬樹
{
if(root == NULL)
{
return;//空樹
}
//按照後序的順序銷燬樹
//如果按照前、中、層序的方法,先銷燬根節點,就會出現找不到其左右孩子節點的情況,造成訪問出錯
TreeDestroy(root->lchild);
TreeDestroy(root->rchild);
DestroyTreeNode(root);
}
size_t TreeSize(TreeNode* root)//樹的大小
{
if(root == NULL)
{
return 0;//空樹
}
return 1+TreeSize(root->lchild)+TreeSize(root->rchild);
}
size_t TreeLeafSize(TreeNode* root)//葉子結點(左右子樹爲空)的大小
{
if(root == NULL)
{
return 0;//空樹
}
if(root->lchild == NULL && root->rchild == NULL)
{
return 1;
}
return TreeLeafSize(root->lchild)+TreeLeafSize(root->rchild);
}
size_t TreeKLevelSize(TreeNode* root,size_t k)//第k層的大小
{
if(root == NULL || k < 1)
{
return 0;//空樹
}
if(k == 1)
{
return 1;
}
return TreeKLevelSize(root->lchild,k-1)+TreeKLevelSize(root->rchild,k-1);
}
size_t TreeHeight(TreeNode* root)//樹的高度
{
if(root == NULL)
{
return 0;//空樹
}
if(root->lchild == NULL && root->rchild == NULL)
{
return 1;
}
//以左右子樹中比較高的子樹的高度作爲樹的高度
size_t lheight = TreeHeight(root->lchild);
size_t rheight = TreeHeight(root->rchild);
//先比較左右子樹哪個高,最後用較高的加上根結點
return 1+(lheight > rheight ? lheight : rheight);
}
//在樹中查找某個值
TreeNode* TreeFind(TreeNode* root,TreeNodeType to_find)
{
if(root == NULL)
{
return NULL;//空樹
}
if(root->data == to_find)
{
return root;//找到了,返回此結點
}
TreeNode* lresult = TreeFind(root->lchild,to_find);
TreeNode* rresult = TreeFind(root->rchild,to_find);
return lresult != NULL ? lresult:rresult;
}
//找一個結點的父結點
TreeNode* ParentTree(TreeNode* root,TreeNode* child)
{
if(root == NULL || child == NULL)
{
return NULL;//root爲空是空樹,child爲空,非法輸入
}
if(root->lchild == child ||root->rchild == child)
{
return root;
}
TreeNode* lresult = ParentTree(root->lchild,child);
TreeNode* rresult = ParentTree(root->rchild,child);
return lresult != NULL ? lresult:rresult;
}
//找一個結點的左孩子節點
TreeNode* LChildNode(TreeNode* root,TreeNode* lchild)
{
if(root == NULL)
{
return NULL;//空樹
}
if(root == lchild)
{
return root->lchild;
}
return LChildNode(root->lchild,lchild);
}
//找一個結點的右孩子結點
TreeNode* RChildNode(TreeNode* root,TreeNode* rchild)
{
if(root == NULL)
{
return NULL;//空樹
}
if(root == rchild)
{
return root->rchild;
}
return RChildNode(root->rchild,rchild);
}
通過上述操作,我們發現樹是遞歸實現的,因此對其操作時一般也採用遞歸的方法來實現。