顺序存储:
#include <stdio.h>
#include <math.h> //其中floor()函数为向下(左)取整,ceil()函数为向上(右)取整
#define MaxSize 10
typedef int ElemType;
//定义树结点
typedef struct{
ElemType data;
bool isEmpty;
}TreeNode;
//初始化所有结点
void InitTree(TreeNode T[]){
for(int i=0;i<MaxSize;i++){
T[i].isEmpty=true;
}
}
//存入一些数据
bool InputData(TreeNode T[]){
int j=3;
for(int i=1;i<MaxSize;i++){
T[i].data=j;
T[i].isEmpty=false;
j+=2;
}
return true;
}
//查找左孩子
bool Outlchild(TreeNode T[],int i,ElemType &e){
if(i>=MaxSize)
return false;
if(2*i>=MaxSize||T[2*i].isEmpty==true)
return false;
else{
e=T[2*i].data;
}
return true;
}
/查找右孩子
bool Outrchild(TreeNode T[],int i,ElemType &e){
if(i>=MaxSize)
return false;
if(2*i+1>=MaxSize||T[2*i+1].isEmpty==true)
return false;
else{
e=T[2*i+1].data;
}
return true;
}
//查找父节点
bool OutFather(TreeNode T[],int i,ElemType &e){
if(i>=MaxSize)
return false;
if(i/2>=MaxSize||T[i/2].isEmpty==true)
return false;
else{
e=T[i/2].data;
}
return true;
}
//判断i所在的层次
int TreeNodefloor(TreeNode T[],int i){
int m=2,n;
double a=floor(log(i)/log(m))+1;//自定义的:以m为底的log(i)
if(i>=MaxSize)
return -1;
else
return a;
}
//判断i是否为叶子结点
bool isLeaf(TreeNode T[],int i){
if(i>(MaxSize-1)/2)
return true;
else
return false;
}
/main函数测试
int main(){
TreeNode T[MaxSize];
InitTree(T);
InputData(T);
int i=3;
ElemType e;
//查找左孩子
if(Outlchild(T,i,e))
printf("左孩子查找成功\n");
else
printf("左孩子查找失败\n");
printf("%d\n",e);
//查找右孩子
if(Outrchild(T,i,e))
printf("右孩子查找成功\n");
else
printf("右孩子查找失败\n");
printf("%d\n",e);
//查找父节点
if(OutFather(T,i,e))
printf("父节点查找成功\n");
else
printf("父节点查找失败\n");
printf("%d\n",e);
//判断i所在的层次
int floor=TreeNodefloor(T,i);
printf("a=%d\n",floor);
//判断是否为叶子结点
if(isLeaf(T,i))
printf("叶子结点\n");
else
printf("分支结点\n");
}
二叉树的顺序存储中,一定要把二叉树的结点编号与完全二叉树对应起来
i的左孩子 2i
i的右孩子 2i+1
i的父节点[ i/2]
链式存储:
typedef struct BiTNode{
ElemType data;//数据域
struct BiTNode *lchild, *rchild;//左右孩子指针
}BiTNode,*BiTree;
n个结点的二叉链表共有n+1个空链域
二叉链表的创建:
#include <stdio.h>
#include <stdlib.h>
#define TElemType int
typedef struct BiTNode{
TElemType data;//数据域
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
void CreateBiTree(BiTree *T){
*T=(BiTNode*)malloc(sizeof(BiTNode));
(*T)->data=1;
(*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode));
(*T)->rchild=NULL;
(*T)->lchild->data=2;
(*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
(*T)->lchild->rchild=NULL;
(*T)->lchild->lchild->data=3;
(*T)->lchild->lchild->lchild=NULL;
(*T)->lchild->lchild->rchild=NULL;
}
int main() {
BiTree Tree;
CreateBiTree(&Tree);
printf("%d",Tree->lchild->lchild->data);
return 0;
}
二叉树的前中后序遍历:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAXSIZE 100
/**二叉树数据结构定义**/
typedef struct BiTreeNode
{
char data;
struct BiTreeNode *left;
struct BiTreeNode *right;
}BiTreeNode,*BiTree;
/**二叉树的建立--按照先序方式建立--插入**/
void CreateBiTree(BiTree *T)
{
char val;
scanf("%c",&val);
if(val == '#')
*T = NULL; //null表示为空枝
else
{
*T = (BiTree)malloc(sizeof(BiTreeNode));
(*T)->data = val;
CreateBiTree(&(*T)->left);
CreateBiTree(&(*T)->right);
}
}
/**先序遍历 根左右**/
void PreOrderTravel(BiTree T)
{
if(T==NULL)
return;
printf("%c ",T->data);
PreOrderTravel(T->left);
PreOrderTravel(T->right);
}
/**中序遍历 左根右**/
void InOrderTravel(BiTree T)
{
if(T==NULL)
return;
InOrderTravel(T->left);
printf("%c ",T->data);
InOrderTravel(T->right);
}
/**后序遍历 左右根**/
void TailOrderTravel(BiTree T)
{
if(T==NULL)
return;
TailOrderTravel(T->left);
TailOrderTravel(T->right);
printf("%c ",T->data);
}
int main()
{
printf("测试代码\n");
BiTree T;
T = (BiTree)malloc(sizeof(BiTreeNode));
printf("请给二叉树按照先序方式依次输入结点的值(空结点为#):\n");
CreateBiTree(&T);
printf("先序方式遍历结果:\n");
PreOrderTravel(T);
printf("\n");
printf("中序方式遍历结果:\n");
InOrderTravel(T);
printf("\n");
printf("后序方式遍历结果:\n");
TailOrderTravel(T);
printf("\n");
return 0;
}
int treeDepth(BiTree T){//树的深度计算
if(T=NULL){
return 0;
}
else{
int l =treeDepth(T->lchild);
int r =treeDepth(T->rchild);
return l>r ?l+1:r+1;//树的深度等于Max(左子树深度,右子树深度)+1
}
}
线索二叉树:
思路:从根节点出发,重新进行一次中序遍历,指针q记录当前访问的结点,指针pre记录上一个被访问的结点。
1当q=p时,pre为前驱
2当pre==p,q为后继
typedef struct ThreadNode {
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;//tag==0表示指针指向孩子,tag==1,表示指针是线索
线索二叉树
ThreadNode *pre =NULL;//全局变量pre,指向当前访问节点的前驱
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;//tag =1的时候,表明建立线索,tag=0表明存在孩子
}ThreadNode,*ThreadTree;
void CreateInThread(ThreadTree T){
pre =NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL){
pre->rtag=1;
}
}
}
void InThread(ThreadTree T){//中序遍历二叉树,一边遍历一边线索
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild==NULL){//
q->lchild=pre;
q->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=q;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=q;
}
前序遍历(考虑转圈问题)
ThreadNode *pre =NULL;//全局变量pre,指向当前访问节点的前驱
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;//tag =1的时候,表明建立线索,tag=0表明存在孩子
}ThreadNode,*ThreadTree;
void CreateInThread(ThreadTree T){
pre =NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL){//处理最后一个节点
pre->rtag=1;
}
}
}
void PreThread(ThreadTree T){//中序遍历二叉树,一边遍历一边线索
if(T!=NULL){
visit(T);
if(T->ltag==0){//lchild 不是前驱线索
PreThread(T->lchild);
}
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild==NULL){//
q->lchild=pre;
q->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=q;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=q;
}
前序遍历(考虑转圈问题)
ThreadNode *pre =NULL;//全局变量pre,指向当前访问节点的前驱
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;//tag =1的时候,表明建立线索,tag=0表明存在孩子
}ThreadNode,*ThreadTree;
void CreateInThread(ThreadTree T){
pre =NULL;
if(T!=NULL){
PostThread(T);
if(pre->rchild==NULL){//处理最后一个节点
pre->rtag=1;
}
}
}
void PostThread(ThreadTree T){//中序遍历二叉树,一边遍历一边线索
if(T!=NULL){
visit(T);
if(T->ltag==0){//lchild 不是前驱线索
PostThread(T->lchild);
}
PostThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild==NULL){//
q->lchild=pre;
q->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=q;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=q;
}
中序线索二叉树找出中序后继
在这里需要明确的一点是,寻找中序遍历的后继,就是寻找右子树中最左边的结点
ThreadNode * Firstnode(ThreadNode *p){//找到以p为根的子树中,第一个被遍历的结点
while(p->rtag ==0){
p=p->lchild;
}
return p;
}
ThreadNode *Nextnode(Thread *p){//在中序线索二叉树中找到结点p的后继结点
if(p->rtag==0){//未被线索化
return Firstnode(p->rchild);
}
else{
return p->child;//线索化,直接返回后继
}
}
void Inorder(Thread *T){
for(ThreadNode *p =Firstnode(T):p!=NULL:p=Nextnode(p)){
visit(p);
}
}
中序线索二叉树找出中序前驱:
找到前驱的前提,就是代表p(结点)有左子树,如果说明有左子树的话,即找到左子树的最右的结点
//找到以p为根的子树中,最后一个被中序遍历的结点
ThreadNode *Lastnode(ThreadNode *p){
while(p->rtag ==0)
}
ThreadNode *Prenode(ThreadNode *p){//在中序二叉树中找到结点p的前驱结点
if(p->ltag==0){
return Lastnode(p->lchild);
}
else{
return p->rchild;
}
}
对中序线索二叉树进行逆向中序遍历
void RevInorder(ThreadNode *T){
for(Thread *p =Lastnode(T):p!=NULL:p=Prenode(p)){
visit(p);
}
}
在先序线索二叉树中找到结点*p的先序后继next
1若p->rtag==1,则next=p->rchild
2p->rtag==0,说明p一定有右孩子,如果p有左孩子,则先序后继为左孩子
如果p有右孩子,则先序后继为右孩子
在先序线索二叉树中找到结点*p的先序前驱pre
1若p->ltag==1,则next=p->lchild
2p->ltag==0 先序遍历中左右子树中的结点只可能是根的后继,不可能是前驱
采用的方法是从头开始先序遍历
在后序线索二叉树中找后序前驱:
1若p->ltag==1,则pre=p->lchild,
2p->ltag==0(说明一定有左孩子),后序遍历(左右根)假如p有右孩子,则后续前驱为右孩子,假如p没有右孩子,则后续前驱为左孩子,
在后序线索二叉树中找后序后继:
1p->rtag==1,则next =p->rchild
2若p->rtag==0 (后序遍历中,左右子树中的结点只能是根的前驱结点,不可能是后继结点)
解决方法:
1采用土方法进行后序遍历
2三叉链表,
总结