目录
•二叉树的链式存储结构
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild; //左子树结点指针
struct BiTNode *rchild; //右子树结点指针
//添加根结点指针,构成二叉树的三叉链表
//struct BiTNode *parent;
}BiTNode,*BiTree;
•二叉树的遍历
1.先序遍历
//先序遍历(前缀表达式)
void PreOrder(BiTree T){
if(T != NULL){
visit(T); //访问根节点
PreOrder(T->lchild); //访问左节点
PreOrder(T->rchild); //访问右节点
}
}
2.中序遍历
//中序遍历(中缀表达式)
void InOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild); //访问左节点
visit(T); //访问根节点
PreOrder(T->rchild); //访问右节点
}
}
3.中序遍历
//后序遍历(后缀表达式)
void PostOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild); //访问左节点
PreOrder(T->rchild); //访问右节点
visit(T); //访问根节点
}
}
利用递归工作栈进行的三种顺序遍历每个结点都访问且仅访问一次,时间复杂度都是
O
(
n
)
O(n)
O(n);
在最坏情况下,二叉树有n个结点且深度为n,此时空间复杂度为
O
(
n
)
O(n)
O(n)。
4.层次遍历
这里用到的队列相关操作见 《队列的相关操作》
//层次遍历
void LevelOrder(BiTree T){
LinkQueue Q; //创建一个辅助队列
INitQueue(Q); //初始化队列
BiTree p; //存储需要访问的出队结点
EnQueue(Q,T); //根节点入队
while(!ISEmpty(Q)){
//队列非空时,进行循环
DeQueue(Q,p); //队头结点出队
visit(p); //访问出队结点
if(p->lchild != NULL){
EnQueue(Q,p->lchild); //出队结点的左孩子入队
}
if(p->rchild != NULL){
EnQueue(Q,p->rchild); //出队结点的右孩子入队
}
}
}
5.代码汇总
#include <stdio.h>
#include <stdbool.h>
#define ElemType int
//二叉树的链式存储结点
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild; //左子树结点指针
struct BiTNode *rchild; //右子树结点指针
//添加根结点指针,构成二叉树的三叉链表
//struct BiTNode *parent;
}BiTNode,*BiTree;
//-------------------------------------------------------------------
//一个任意的访问函数
void visit(BiTree T) {
/*
* 编写相应的访问函数
*/
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//先序遍历(前缀表达式)
void PreOrder(BiTree T){
if(T != NULL){
visit(T); //访问根节点
PreOrder(T->lchild); //访问左节点
PreOrder(T->rchild); //访问右节点
}
}
//中序遍历(中缀表达式)
void InOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild); //访问左节点
visit(T); //访问根节点
PreOrder(T->rchild); //访问右节点
}
}
//后序遍历(后缀表达式)
void PostOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild); //访问左节点
PreOrder(T->rchild); //访问右节点
visit(T); //访问根节点
}
}
//层次遍历
void LevelOrder(BiTree T){
LinkQueue Q; //创建一个辅助队列
INitQueue(Q); //初始化队列
BiTree p; //存储需要访问的出队结点
EnQueue(Q,T); //根节点入队
while(!ISEmpty(Q)){
//队列非空时,进行循环
DeQueue(Q,p); //队头结点出队
visit(p); //访问出队结点
if(p->lchild != NULL){
EnQueue(Q,p->lchild); //出队结点的左孩子入队
}
if(p->rchild != NULL){
EnQueue(Q,p->rchild); //出队结点的右孩子入队
}
}
}
//-------------------------------------------------------------------
•线索二叉树
//线索二叉树结点
typedef struct ThreadNode{
ElemType data; //数据域
struct ThreadNode *lchild; //左子树结点指针
struct ThreadNode *rchild; //右子树结点指针
bool ltag; //线索判断左右指针是线索还是孩子结点
bool rtag; //true为线索,false为孩子结点
}ThreadNode,*ThreadTree;
1.中序线索化
通过中序遍历进行线索化
//通过中序遍历进行的中序线索化
void InThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对左孩子结点进行线索化
InThread(p->lchild,pre);
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
//对右孩子结点进行线索化
InThread(p->rchild,pre);
}
}
void CreateInThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
InThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
中序线索二叉树的遍历
//求中序序列下的第一个结点
ThreadNode *FirestNode(ThreadNode *p){
while(p->ltag == false){
//当前结点有左孩子,则往前继续找
p = p->lchild;
}
return p;
}
//求当前结点的中序后继
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag == false){
//当前结点有右子树,则返回右子树的第一个结点
return FirestNode(p->rchild);
}else{
//当前结点右子树为空,直接返回后继结果
return p->rchild;
}
}
由上面两个函数,可以构成不含头结点的中序线索二叉树的中序遍历算法
//中序线索二叉树的中序遍历
void ThreadInOrder(ThreadTree T){
for(ThreadNode *p = FirestNode(T); p != NULL; p = NextNode(p)){
//创建结点p初始化为T树的第一个结点,逐次访问其后继
visit(p);
}
}
2.先序线索化
大致操作与中序相同,修改各个部分线索化顺序即可
//后序线索二叉树
//通过先序遍历进行的先序线索化
void PreThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
//对左孩子结点进行线索化
if(p->ltag == false){
//左子树不是其前驱线索时,才进行线索化
PreThread(p->lchild,pre);
}
//对右孩子结点进行线索化
PreThread(p->rchild,pre);
}
}
void CreatePreThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
PreThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
3.后序线索化
//后序线索二叉树
//通过后序遍历进行的后序线索化
void PostThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对左孩子结点进行线索化
PostThread(p->lchild,pre);
//对右孩子结点进行线索化
PostThread(p->rchild,pre);
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
}
}
void CreatePostThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
PostThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
4.代码汇总
#include <stdio.h>
#include <stdbool.h>
#define ElemType int
//线索二叉树结点
typedef struct ThreadNode{
ElemType data; //数据域
struct ThreadNode *lchild; //左子树结点指针
struct ThreadNode *rchild; //右子树结点指针
bool ltag; //线索判断左右指针是线索还是孩子结点
bool rtag; //true为线索,false为孩子结点
}ThreadNode,*ThreadTree;
//-------------------------------------------------------------------
//中序线索二叉树
//通过中序遍历进行的中序线索化
void InThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对左孩子结点进行线索化
InThread(p->lchild,pre);
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
//对右孩子结点进行线索化
InThread(p->rchild,pre);
}
}
void CreateInThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
InThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
//求中序序列下的第一个结点
ThreadNode *FirestNode(ThreadNode *p){
while(p->ltag == false){
//当前结点有左孩子,则往前继续找
p = p->lchild;
}
return p;
}
//求当前结点的中序后继
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag == false){
//当前结点有右子树,则返回右子树的第一个结点
return FirestNode(p->rchild);
}else{
//当前结点右子树为空,直接返回后继结果
return p->rchild;
}
}
//中序线索二叉树的中序遍历
void ThreadInOrder(ThreadTree T){
for(ThreadNode *p = FirestNode(T); p != NULL; p = NextNode(p)){
//创建结点p初始化为T树的第一个结点,逐次访问其后继
visit(p);
}
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//先序线索二叉树
//通过先序遍历进行的先序线索化
void PreThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
//对左孩子结点进行线索化
if(p->ltag == false){
//左子树不是其前驱线索时,才进行线索化
PreThread(p->lchild,pre);
}
//对右孩子结点进行线索化
PreThread(p->rchild,pre);
}
}
void CreatePreThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
PreThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//后序线索二叉树
//通过后序遍历进行的后序线索化
void PostThread(ThreadNode *p, ThreadNode *pre){
//结点非空才能进行线索化
if(p != NULL){
//对左孩子结点进行线索化
PostThread(p->lchild,pre);
//对右孩子结点进行线索化
PostThread(p->rchild,pre);
//对根结点进行线索化
if(p->lchild == NULL){
//左孩子为空,则建立前驱线索
p->lchild = pre;
p->ltag = true;
}
if(pre != NULL && pre->rchild == NULL){
//前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
pre->rchild = p;
pre->rtag = true;
}
pre = p; //更新前驱结点为当前结点
}
}
void CreatePostThread(ThreadTree T){
ThreadNode *pre = NULL; //指向被访问结点的前驱结点
if(T != NULL){
//非空二叉树才能进行线索化
PostThread(T,pre); //中序线索化
//处理最后一个结点
if(pre->rchild == NULL){
pre->rtag = 1;
}
}
}
//-------------------------------------------------------------------
•二叉排序树
1.查找
有递归与非递归两种
//非递归查找
BSTNode *BST_Search(BSTree T, ElemType key) {
while (T != NULL && key != T->data) {
//当树结点T非空且T值不是key值时进行循环判定
if (key < T->data) {
T = T->lchild; //key值比T值小,继续向T的左子树寻找
}else{
T = T->rchild; //key值比T值大,继续向T的右子树寻找
}
}
return T; //返回符合key的结点T,没有找到则T为空
}
//递归查找
BSTNode *BST_Search1(BSTree T, ElemType key){
if(T == NULL){
return NULL; //查找失败
}
if(key == T->data){
return T; //查找成功
}
if (key < T->data) {
return BST_Search1(T->lchild, key); //key值比T值小,继续向T的左子树寻找
}
if(key > T->data){
return BST_Search1(T->rchild, key); //key值比T值大,继续向T的右子树寻找
}
}
2.插入
和查找的方法差不多,也是查找找到正确位置以后增加一个插入操作
循环进行插入操作,就可以构建一个二叉排序树
//非递归插入
bool BST_Insert(BSTree T, ElemType key) {
while (T != NULL && key != T->data) {
//当树结点T非空,则继续移动
if (key < T->data) {
T = T->lchild; //key值比T值小,继续向T的左子树移动
} else {
T = T->rchild; //key值比T值大,继续向T的右子树移动
}
}
if(key == T->data){
return false; //存在关键字相同的结点,插入失败
}else{
//找到正确位置的空结点T以后,进行插入操作
T = (BSTree) malloc(sizeof(BSTNode));
T->data = key;
T->lchild = NULL;
T->rchild = NULL;
return true;
}
}
//递归插入
bool BST_Insert1(BSTree T, ElemType key) {
if (T == NULL) {
T = (BSTree) malloc(sizeof(BSTNode));
T->data = key;
T->lchild = NULL;
T->rchild = NULL;
return true;
}
if (key == T->data) {
return false; //存在关键字相同的结点,插入失败
}
if (key < T->data) {
return BST_Insert1(T->lchild, key); //key值比T值小,继续向T的左子树插入
}
if (key > T->data) {
return BST_Insert1(T->rchild, key); //key值比T值大,继续向T的右子树插入
}
}
3.代码汇总
#include <stdio.h>
#include <stdbool.h>
#include <malloc.h>
#define ElemType int
//二叉排序树的链式存储结点
typedef struct BSTNode {
ElemType data; //数据域
struct BSTNode *lchild, *rchild; //左右子树结点指针
} BSTNode, *BSTree;
//------------------------------------------------------------------
//非递归查找
BSTNode *BST_Search(BSTree T, ElemType key) {
while (T != NULL && key != T->data) {
//当树结点T非空且T值不是key值时进行循环判定
if (key < T->data) {
T = T->lchild; //key值比T值小,继续向T的左子树寻找
} else {
T = T->rchild; //key值比T值大,继续向T的右子树寻找
}
}
return T; //返回符合key的结点T,没有找到则T为空
}
//递归查找
BSTNode *BST_Search1(BSTree T, ElemType key) {
if (T == NULL) {
return NULL; //查找失败
}
if (key == T->data) {
return T; //查找成功
}
if (key < T->data) {
return BST_Search1(T->lchild, key); //key值比T值小,继续向T的左子树寻找
}
if (key > T->data) {
return BST_Search1(T->rchild, key); //key值比T值大,继续向T的右子树寻找
}
}
//------------------------------------------------------------------
//------------------------------------------------------------------
//非递归插入
bool BST_Insert(BSTree T, ElemType key) {
while (T != NULL && key != T->data) {
//当树结点T非空,则继续移动
if (key < T->data) {
T = T->lchild; //key值比T值小,继续向T的左子树移动
} else {
T = T->rchild; //key值比T值大,继续向T的右子树移动
}
}
if(key == T->data){
return false; //存在关键字相同的结点,插入失败
}else{
//找到正确位置的空结点T以后,进行插入操作
T = (BSTree) malloc(sizeof(BSTNode));
T->data = key;
T->lchild = NULL;
T->rchild = NULL;
return true;
}
}
//递归插入
bool BST_Insert1(BSTree T, ElemType key) {
if (T == NULL) {
T = (BSTree) malloc(sizeof(BSTNode));
T->data = key;
T->lchild = NULL;
T->rchild = NULL;
return true;
}
if (key == T->data) {
return false; //存在关键字相同的结点,插入失败
}
if (key < T->data) {
return BST_Insert1(T->lchild, key); //key值比T值小,继续向T的左子树插入
}
if (key > T->data) {
return BST_Insert1(T->rchild, key); //key值比T值大,继续向T的右子树插入
}
}
//------------------------------------------------------------------