数据结构之线索二叉树——C语言实现
线索二叉树操作
(1) 线索二叉树的表示:将每个节点中为空的做指针与右指针分别用于指针节点的前驱和后续,即可得到线索二叉树。
(2) 分类:先序线索二叉树,中序线索二叉树,后续线索二叉树
(3) 增加线索标志域后,二叉链表的结构如下:
typedef enum { SubTree, Thread }NodeFlag; //枚举值SubTree(子树)和Thread(线索)分别为0,1 typedef struct ThreadTree //定义线索二叉树结点类型 { DATA data; //元素数据 NodeFlag lflag; //左标志 NodeFlag rflag; //右标志 struct ThreadTree *left; //左子树结点指针 struct ThreadTree *right; //右子树结点指针 }ThreadBinTree;
(4) 操作线索二叉树:
a) 查找后续节点:
i. 若父节点P的右子树为空,则p->right为右线索,直接指向p的中序后续。
ii. 若父节点p的右子树不为空,则p的中序后续必是其右子树中第一个中序遍历到的节点。
b) 查找前驱节点:
i. 若父节点p的左子树为空,则p->left为左线索,直接指向p的中序前驱节点
ii. 若父节点p的左子树不为空,则从p的左子树出发,沿该子树的右指针链表往下查找,直到一个没有右子树的节点为止,则该及诶按就是p 的中序前驱节点。
c) 遍历线索二叉树:由于二叉树被线索化,遍历时可根据后续指针快速完成,而不使用递归调用即可完成。
(5) 对二叉树进行中序线索化。C语言实现代码如下:
/* ******************************************************************************** * 数据结构 * * * * 作者: zhi-z * * QQ号: 2282643301 * ******************************************************************************** *文件名 : ThreadBinTree.c *作用 : 线索二叉树 操作 *原理 : ******************************************************************************** *版本 作者 日期 说明 *V0.1 2016-10-3 初始版本 ******************************************************************************** */ #include <stdio.h> #include <stdlib.h> #define QUEUE_MAXSIZE 50 /******************************************************************************** *数据结构名 : ThreadTree *参数 : *作用 : 定义线索二叉树的数据结构 *内容 : * ********************************************************************************/ typedef char DATA; //定义元素类型 typedef enum { SubTree, Thread }NodeFlag; //枚举值SubTree(子树)和Thread(线索)分别为0,1 typedef struct ThreadTree //定义线索二叉树结点类型 { DATA data; //元素数据 NodeFlag lflag; //左标志 NodeFlag rflag; //右标志 struct ThreadTree *left; //左子树结点指针 struct ThreadTree *right; //右子树结点指针 }ThreadBinTree; ThreadBinTree *Previous=NULL; //前驱结点指针 /******************************************************************************** *函数名 : BinTreeInit *参数 :node:线索二叉树的一个节点 *作用 : 初始化线索二叉树 *内容 : * ********************************************************************************/ ThreadBinTree *BinTreeInit(ThreadBinTree *node) //初始化二叉树根结点 { if(node!=NULL) //若二叉树根结点不为空 return node; else return NULL; } /******************************************************************************** *函数名 : BinTreeAddNode *参数 :bt为父结点,node为子结点,n=1表示添加左子树,n=2表示添加右子树 *作用 : 添加数据到二叉树 *内容 : * ********************************************************************************/ int BinTreeAddNode(ThreadBinTree *bt,ThreadBinTree *node,int n) //添加数据到二叉树 //bt为父结点,node为子结点,n=1表示添加左子树,n=2表示添加右子树 { if(bt==NULL) { printf("父结点不存在,请先设置父结点!\n"); return 0; } switch(n) { case 1: //添加到左结点 if(bt->left) //左子树不为空 { printf("左子树结点不为空!\n"); return 0; }else bt->left=node; break; case 2://添加到右结点 if( bt->right) //右子树不为空 { printf("右子树结点不为空!\n"); return 0; }else bt->right=node; break; default: printf("参数错误!\n"); return 0; } return 1; } /******************************************************************************** *函数名 : BinTreeLeft *参数 :bt:为父结点 *作用 : 返回左子结点 *内容 : * ********************************************************************************/ ThreadBinTree *BinTreeLeft(ThreadBinTree *bt) //返回左子结点 { if(bt) return bt->left; else return NULL; } /******************************************************************************** *函数名 : BinTreeRight *参数 :bt:为父结点 *作用 : 返回右子结点 *内容 : * ********************************************************************************/ ThreadBinTree *BinTreeRight(ThreadBinTree *bt) //返回右子结点 { if(bt) return bt->right; else return NULL; } /******************************************************************************** *函数名 : BinTreeIsEmpty *参数 :bt:为父结点 *作用 : 检查二叉树是否为空,为空则返回1,否则返回0 *内容 :判断线索二叉树是否为空(操作与二叉树相同) * ********************************************************************************/ int BinTreeIsEmpty(ThreadBinTree *bt) //检查二叉树是否为空,为空则返回1,否则返回0 { if(bt) return 0; else return 1; } /******************************************************************************** *函数名 : BinTreeDepth *参数 :bt:为父结点 *作用 : 求二叉树深度 *内容 :(操作与二叉树相同) * ********************************************************************************/ int BinTreeDepth(ThreadBinTree *bt) //求二叉树深度 { int dep1,dep2; if(bt==NULL) return 0; //对于空树,深度为0 else { dep1 = BinTreeDepth(bt->left); //左子树深度 (递归调用) dep2 = BinTreeDepth(bt->right); //右子树深度 (递归调用) if(dep1>dep2) return dep1 + 1; else return dep2 + 1; } } /******************************************************************************** *函数名 : BinTreeFind *参数 :bt:为父结点,data:要查找的数据 *作用 : 在二叉树中查找值为data的结点 *内容 :(操作与二叉树相同) * ********************************************************************************/ ThreadBinTree *BinTreeFind(ThreadBinTree *bt,DATA data) //在二叉树中查找值为data的结点 { ThreadBinTree *p; if(bt==NULL) return NULL; else { if(bt->data==data) return bt; else{ // 分别向左右子树递归查找 if(p=BinTreeFind(bt->left,data)) return p; else if(p=BinTreeFind(bt->right, data)) return p; else return NULL; } } } /******************************************************************************** *函数名 : BinTreeClear *参数 :bt:为父结点 *作用 : 清空二叉树,使之变为一棵空树 *内容 :(操作与二叉树相同) * ********************************************************************************/ void BinTreeClear(ThreadBinTree *bt) // 清空二叉树,使之变为一棵空树 { if(bt) { BinTreeClear(bt->left); //清空左子树 BinTreeClear(bt->right);//清空右子树 free(bt);//释放当前结点所占内存 bt=NULL; } return; } /******************************************************************************** *函数名 : BinTree_DLR *参数 :bt:为父结点,(*oper)(ThreadBinTree *p):二叉树的操作 *作用 : 先序遍历 *内容 : * ********************************************************************************/ void BinTree_DLR(ThreadBinTree *bt,void (*oper)(ThreadBinTree *p)) //先序遍历 { if(bt)//树不为空,则执行如下操作 { oper(bt); //处理结点的数据 BinTree_DLR(bt->left,oper); BinTree_DLR(bt->right,oper); } return; } /******************************************************************************** *函数名 : BinTree_LDR *参数 :bt:为父结点,(*oper)(ThreadBinTree *p):二叉树的操作 *作用 : 中序遍历 *内容 : * ********************************************************************************/ void BinTree_LDR(ThreadBinTree *bt,void(*oper)(ThreadBinTree *p)) //中序遍历 { if(bt)//树不为空,则执行如下操作 { BinTree_LDR(bt->left,oper); //中序遍历左子树 oper(bt);//处理结点数据 BinTree_LDR(bt->right,oper); //中序遍历右子树/ } return; } /******************************************************************************** *函数名 : BinTree_LRD *参数 :bt:为父结点,(*oper)(ThreadBinTree *p):二叉树的操作 *作用 : 后序遍历 *内容 : * ********************************************************************************/ void BinTree_LRD(ThreadBinTree *bt,void (*oper)(ThreadBinTree *p)) //后序遍历 { if(bt) { BinTree_LRD(bt->left,oper); //后序遍历左子树 BinTree_LRD(bt->right,oper); //后序遍历右子树/ oper(bt); //处理结点数据 } return; } /******************************************************************************** *函数名 : BinTree_Level *参数 :bt:为父结点,(*oper)(ThreadBinTree *p):二叉树的操作 *作用 : 按层遍历 *内容 : * ********************************************************************************/ void BinTree_Level(ThreadBinTree *bt,void (*oper)(ThreadBinTree *p)) //按层遍历 { ThreadBinTree *p; ThreadBinTree *q[QUEUE_MAXSIZE]; //定义一个顺序栈 int head=0,tail=0;//队首、队尾序号 if(bt)//若队首指针不为空 { tail=(tail+1)%QUEUE_MAXSIZE;//计算循环队列队尾序号 q[tail] = bt;//将二叉树根指针进队 } while(head!=tail) //队列不为空,进行循环 { head=(head+1)%QUEUE_MAXSIZE; //计算循环队列的队首序号 p=q[head]; //获取队首元素 oper(p);//处理队首元素 if(p->left!=NULL) //若结点存在左子树,则左子树指针进队 { tail=(tail+1)%QUEUE_MAXSIZE;//计算循环队列的队尾序号 q[tail]=p->left;//将左子树指针进队 } if(p->right!=NULL)//若结点存在右孩子,则右孩子结点指针进队 { tail=(tail+1)%QUEUE_MAXSIZE;//计算循环队列的队尾序号 q[tail]=p->right;//将右子树指针进队 } } return; } /******************************************************************************** *函数名 : BinTreeThreading_LDR *参数 :bt:为父结点 *作用 : 二叉树按中序线索化 *内容 : * ********************************************************************************/ void BinTreeThreading_LDR(ThreadBinTree *bt) //二叉树按中序线索化 { if(bt) //结点非空时,当前访问结点 { BinTreeThreading_LDR(bt->left); //递归调用,将左子树线索化 bt->lflag=(bt->left)?SubTree:Thread; //设置左指针域的标志 bt->rflag=(bt->right)?SubTree:Thread;//设置右指针域的标志 if(Previous) //若当前结点的前驱Previous存在 { if(Previous->rflag==Thread) //若当前结点的前驱右标志为线索 Previous->right=bt;//设Previous的右线索指向后继 if(bt->lflag==Thread) //若当前结点的左标志为线索 bt->left=Previous;//设当前结点的左线索指向中序前驱 } Previous=bt;//让Previous保存刚访问的结点 BinTreeThreading_LDR(bt->right);//递归调用,将右子树线索化 } } /******************************************************************************** *函数名 : BinTreeNext_LDR *参数 :bt:为父结点 *作用 : 求指定结点的后继 *内容 : * ********************************************************************************/ ThreadBinTree *BinTreeNext_LDR(ThreadBinTree *bt) //求指定结点的后继 { ThreadBinTree *nextnode; if(!bt) return NULL; //若当前结点为空,则返回空 if(bt->rflag==Thread) //若当前结点的右子树为空 return bt->right; //返回右线索所指的中序后继 else{ nextnode=bt->right; //从当前结点的右子树开始查找 while(nextnode->lflag==SubTree) //循环处理所有左子树不为空的结点 nextnode=nextnode->left; return nextnode; //返回左下方的结点 } } /******************************************************************************** *函数名 : BinTreePrevious_LDR *参数 :bt:为父结点 *作用 : 求指定结点的前驱 *内容 : * ********************************************************************************/ ThreadBinTree *BinTreePrevious_LDR(ThreadBinTree *bt) //求指定结点的前驱 { ThreadBinTree *prenode; if(!bt) return NULL; //若当前结点为空,则返回空 if(bt->lflag==Thread) //若当前结点的左子树为空 return bt->left; //返回左线索所指的中序后继 else{ prenode=bt->left; //从当前结点的左子树开始查找 while(prenode->rflag==SubTree) //循环处理所有右子树不为空的结点 prenode=prenode->left; return prenode; //返回左下方的结点 } } /******************************************************************************** *函数名 : ThreadBinTree_LDR *参数 :bt:为父结点 ,(*oper)(ThreadBinTree *p):二叉树的操作 *作用 :遍历中序线索二叉树 *内容 : * ********************************************************************************/ void ThreadBinTree_LDR(ThreadBinTree *bt,void (*oper)(ThreadBinTree *p)) //遍历中序线索二叉树 { if(bt) //二叉树不为空 { while(bt->lflag==SubTree)//有左子树 bt=bt->left; //从根往下找最左下结点,即中序序列的开始结点 do{ oper(bt); //处理结点 bt=BinTreeNext_LDR(bt);//找中序后继结点 }while(bt); } }