目录
二叉树的层次建树 (必须掌握)
二叉树结构体定义
辅助建树的队列的结构体
建树的主要代码
二叉树的遍历(必须掌握)
前序遍历
中序遍历
后序遍历
非递归实现中序遍历
层次遍历
完整代码
线索二叉树
结构体定义
建立二叉搜索树
完整代码
二叉排序树(二叉查找树)
定义
结构体定义
创建二叉排序树
插入结点操作
查找结点值为key的元素
删除结点
完整代码
树的性质
二叉树五种形态
//二叉树结构体定义
typedef struct BiTNode {
BiElemType c; //数据域
struct BiTNode* lchild; //左孩子指针
struct BiTNode* rchild; //右孩子指针
}BiTNode, * BiTree;
typedef struct tag
{
BiTree p; //树的某一个结点的地址值
struct tag* pnext;
}tag_t, * ptag_t;
BiTree pnew;
int i, j, pos;
char c;
BiTree tree = NULL; //树根置空
ptag_t phead = NULL, ptail = NULL, listpnew = NULL, pcur = NULL;//phead就是队列头,ptail就是队列尾
//abcdefghij
while (scanf("%c", &c) != EOF)
{
if (c == '\n')
{
break;
}
pnew = (BiTree)calloc(1, sizeof(BiTNode)); //calloc申请空间并对空间进行初始化,赋值为0
pnew->c = c;//数据放进去
listpnew = (ptag_t)calloc(1, sizeof(tag_t));//给队列结点申请空间
listpnew->p = pnew;
if (tree == NULL)
{
tree = pnew;//树的根
phead = listpnew;//队列头
ptail = listpnew;//队列尾
pcur = listpnew;
continue;
}
else
{
ptail->pnext = listpnew;//新结点放入链表,通过尾插法
ptail = listpnew;//ptail指向队列尾部
}//pcur始终指向要插入的结点的位置
if (pcur->p->lchild == NULL)//如何把新结点放入树,也就是判断根节点的左边为空
{
pcur->p->lchild = pnew;//把新结点放到要插入结点的左边
}
else if (pcur->p->rchild == NULL)
{
pcur->p->rchild = pnew; //把新结点放到要插入结点的右边
pcur = pcur->pnext; //左右都放了结点后,pcur指向队列的下一个
}
}
采用递归实现
遍历的示意图
//前序遍历,前序遍历就是深度优先遍历
void preOrder(BiTree p)
{
if (p != NULL)
{
putchar(p->c);//等价于visit函数 ,打印根节点
preOrder(p->lchild); //打印左子树
preOrder(p->rchild); //打印右子树
}
}
//中序遍历
void InOrder(BiTree p)
{
if (p != NULL)
{
InOrder(p->lchild);//打印左子树
putchar(p->c); //打印根节点
InOrder(p->rchild); //打印右子树
}
}
// 后序遍历
void PostOrder(BiTree p)
{
if (p != NULL)
{
PostOrder(p->lchild); //打印左子树
PostOrder(p->rchild);//打印右子树
putchar(p->c); //打印根结点
}
}
//中序遍历非递归,非递归执行效率更高
void InOrder2(BiTree T)
{
SqStack S;
InitStack(S);
BiTree p = T;
while (p || !StackEmpty(S))//逻辑或||
{
if (p)
{//当一个结点不为空,压栈,并取左孩子
Push(S, p);
p = p->lchild;
}
else {//弹出栈中元素并打印,获取打印元素的右结点
Pop(S, p); putchar(p->c);
p = p->rchild;
}
}
}
//层次遍历,层序遍历,广度优先遍历
void LevelOrder(BiTree T)
{
LinkQueue Q;//辅助队列
InitQueue(Q);//初始化队列
BiTree p;
EnQueue(Q, T);//树根入队
while (!IsEmpty(Q))
{
DeQueue(Q, p);//出队当前结点并打印
putchar(p->c);
if (p->lchild != NULL) //入队左孩子
EnQueue(Q, p->lchild);
if (p->rchild != NULL) //入队右孩子
EnQueue(Q, p->rchild);
}
}
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include "function.h"
typedef char BiElemType;
//二叉树结构体定义
typedef struct BiTNode {
BiElemType c; //数据域
struct BiTNode* lchild; //左孩子指针
struct BiTNode* rchild; //右孩子指针
}BiTNode, * BiTree;
//辅助建树的队列
typedef struct tag
{
BiTree p; //树的某一个结点的地址值
struct tag* pnext;
}tag_t, * ptag_t;
//递归实现
//abdhiejcfg 前序遍历,前序遍历就是深度优先遍历
void preOrder(BiTree p)
{
if (p != NULL)
{
putchar(p->c);//等价于visit函数 ,打印根节点
preOrder(p->lchild); //打印左子树
preOrder(p->rchild); //打印右子树
}
}
//中序遍历 hdibjeafcg
void InOrder(BiTree p)
{
if (p != NULL)
{
InOrder(p->lchild);//打印左子树
putchar(p->c); //打印根节点
InOrder(p->rchild); //打印右子树
}
}
//hidjebfgca 后序遍历
void PostOrder(BiTree p)
{
if (p != NULL)
{
PostOrder(p->lchild); //打印左子树
PostOrder(p->rchild);//打印右子树
putchar(p->c); //打印根结点
}
}
//中序遍历非递归,非递归执行效率更高,考的概率很低
void InOrder2(BiTree T)
{
SqStack S;
InitStack(S);
BiTree p = T;
while (p || !StackEmpty(S))//逻辑或||
{
if (p)
{//当一个结点不为空,压栈,并取左孩子
Push(S, p);
p = p->lchild;
}
else {//弹出栈中元素并打印,获取打印元素的右结点
Pop(S, p); putchar(p->c);
p = p->rchild;
}
}
}
//层次遍历,层序遍历,广度优先遍历
void LevelOrder(BiTree T)
{
LinkQueue Q;//辅助队列
InitQueue(Q);//初始化队列
BiTree p;
EnQueue(Q, T);//树根入队
while (!IsEmpty(Q))
{
DeQueue(Q, p);//出队当前结点并打印
putchar(p->c);
if (p->lchild != NULL) //入队左孩子
EnQueue(Q, p->lchild);
if (p->rchild != NULL) //入队右孩子
EnQueue(Q, p->rchild);
}
}
int main()
{
BiTree pnew;
int i, j, pos;
char c;
BiTree tree = NULL; //树根置空
ptag_t phead = NULL, ptail = NULL, listpnew = NULL, pcur = NULL;//phead就是队列头,ptail就是队列尾
//abcdefghij
while (scanf("%c", &c) != EOF)
{
if (c == '\n')
{
break;
}
pnew = (BiTree)calloc(1, sizeof(BiTNode)); //calloc申请空间并对空间进行初始化,赋值为0
pnew->c = c;//数据放进去
listpnew = (ptag_t)calloc(1, sizeof(tag_t));//给队列结点申请空间
listpnew->p = pnew;
if (tree == NULL)
{
tree = pnew;//树的根
phead = listpnew;//队列头
ptail = listpnew;//队列尾
pcur = listpnew;
continue;
}
else
{
ptail->pnext = listpnew;//新结点放入链表,通过尾插法
ptail = listpnew;//ptail指向队列尾部
}//pcur始终指向要插入的结点的位置
if (pcur->p->lchild == NULL)//如何把新结点放入树,也就是判断根节点的左边为空
{
pcur->p->lchild = pnew;//把新结点放到要插入结点的左边
}
else if (pcur->p->rchild == NULL)
{
pcur->p->rchild = pnew; //把新结点放到要插入结点的右边
pcur = pcur->pnext; //左右都放了结点后,pcur指向队列的下一个
}
}
printf("--------前序遍历----------\n");//也叫先序遍历,先打印当前结点,打印左孩子,打印右孩子
preOrder(tree); //根+左+右
printf("\n--------中序遍历------------\n");//先打印左孩子,打印父亲,打印右孩子
InOrder(tree); //左+根+右
printf("\n--------后序遍历------------\n");//先打印左孩子,打印右孩子,最后打印父亲
PostOrder(tree); //左+右+根
printf("\n--------中序遍历非递归------\n");//重要性低
InOrder2(tree);
printf("\n--------层次遍历-----------\n");
LevelOrder(tree);
printf("\n");
return 0;
}
概念:当一个结点的左孩子指针或者右孩子指针是空着的,线索二叉树就是不让结点的左右孩子指针空着。
typedef struct ThreadNode {
ElemType data; //数据域
struct ThreadNode* lchild, * rchild; //左孩子指针,右孩子指针
int ltag, rtag;
}ThreadNode, * ThreadTree;
void BulidThreadTree(ThreadTree& T)
{
ThreadTree arr[5];
int i;
for (i = 0; i < 5; i++)
{
arr[i] = (ThreadTree)malloc(sizeof(ThreadNode));
memset(arr[i], 0, sizeof(ThreadNode));
arr[i]->data = 'A' + i;
}
arr[0]->lchild = arr[1];
arr[0]->rchild = arr[2];
arr[1]->rchild = arr[3];
arr[2]->lchild = arr[4];
T = arr[0];
}
#include
#include
#include
typedef char ElemType;
typedef struct ThreadNode {
ElemType data; //数据域
struct ThreadNode* lchild, * rchild; //左孩子指针,右孩子指针
int ltag, rtag;
}ThreadNode, * ThreadTree;
//手工建线索树,总计5个结点
void BulidThreadTree(ThreadTree& T)
{
ThreadTree arr[5];
int i;
for (i = 0; i < 5; i++)
{
arr[i] = (ThreadTree)malloc(sizeof(ThreadNode));
memset(arr[i], 0, sizeof(ThreadNode));
arr[i]->data = 'A' + i;
}
arr[0]->lchild = arr[1];
arr[0]->rchild = arr[2];
arr[1]->rchild = arr[3];
arr[2]->lchild = arr[4];
T = arr[0];
}
void InThread(ThreadTree& p, ThreadTree& pre)
{
if (p != NULL) {
InThread(p->lchild, pre);//递归找树的左孩子
if (p->lchild == NULL) {//左边为NULL,填写当前结点的前驱
p->lchild = pre;
p->ltag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
//pre节点右孩子为NULL,就让其指向后继节点,而后继结点刚好就是p
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
InThread(p->rchild, pre);
}
}
void CreateInThread(ThreadTree T)
{
ThreadTree pre = NULL;//使用辅助指针pre
if (T != NULL) {
InThread(T, pre);
pre->rchild = NULL;
pre->rtag = 1;
}
}
//中序序列下的第一个结点
ThreadNode* Firstnode(ThreadNode* p)
{
while (p->ltag == 0)
p = p->lchild;
return p;
}
//p在中序序列下的后继结点
int main()
{
ThreadTree T;
ThreadTree p;
BulidThreadTree(T);
CreateInThread(T);//构建线索二叉树
p = Firstnode(T);
printf("最左下结点值为 %c\n", p->data);
system("pause");
}
typedef struct BSTNode{
KeyType key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
T=NULL;
int i=0;
while(i
//插入操作,递归实现
int BST_Insert(BiTree &T,KeyType k)
{
if(T==NULL)
{
T=(BiTree)malloc(sizeof(BSTNode)); //为新节点申请空间
T->key=k; //
T->lchild=T->rchild=NULL; //左孩子右孩子为空
return 1;//代表插入成功
}
else if(k==T->key)
return 0;//发现相同元素,就不插入
else if(kkey) //如果要插入的结点,小于当前结点
return BST_Insert(T->lchild,k); //放入左孩子中,递归实现
else
return BST_Insert(T->rchild,k); //放入右孩子中,递归实现
}
//查找结点值为key的元素,非递归实现的
BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{
p=NULL;
while(T!=NULL&&key!=T->key)
{
p=T;
if(keykey)
T=T->lchild;//比当前节点小,就左边找
else
T=T->rchild;//比当前节点大,右边去
}
return T;
}
//删除
void DeleteNode(BiTree &root,KeyType x){
if(root == NULL){
return;
}
if(root->key>x){
DeleteNode(root->lchild,x);
}else if(root->keyrchild,x);
}else{ //查找到了删除节点
if(root->lchild == NULL){ //左子树为空
BiTree tempNode = root;
root = root->rchild;
free(tempNode);
}else if(root->rchild == NULL){ //右子树为空
BiTree tempNode = root;//临时指针
root = root->lchild;
free(tempNode);
}else{ //左右子树都不为空
//一般的删除策略是左子树的最大数据 或 右子树的最小数据 代替要删除的节点(这里采用查找左子树最大数据来代替)
BiTree tempNode = root->lchild;
if(tempNode->rchild!=NULL){
tempNode = tempNode->rchild;
}
root->key = tempNode->key;
DeleteNode(root->lchild,tempNode->key);
}
}
}
#include
#include
typedef int KeyType;
typedef struct BSTNode{
KeyType key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//54,20,66,40,28,79,58
//插入操作,递归实现
int BST_Insert(BiTree &T,KeyType k)
{
if(T==NULL)
{
T=(BiTree)malloc(sizeof(BSTNode)); //为新节点申请空间
T->key=k; //
T->lchild=T->rchild=NULL; //左孩子右孩子为空
return 1;//代表插入成功
}
else if(k==T->key)
return 0;//发现相同元素,就不插入
else if(kkey) //如果要插入的结点,小于当前结点
return BST_Insert(T->lchild,k); //放入左孩子中,递归实现
else
return BST_Insert(T->rchild,k); //放入右孩子中,递归实现
}
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
T=NULL;
int i=0;
while(ikey)
{
p=T;
if(keykey)
T=T->lchild;//比当前节点小,就左边找
else
T=T->rchild;//比当前节点大,右边去
}
return T;
}
//这个书上没有二叉排序树
void DeleteNode(BiTree &root,KeyType x){
if(root == NULL){
return;
}
if(root->key>x){
DeleteNode(root->lchild,x);
}else if(root->keyrchild,x);
}else{ //查找到了删除节点
if(root->lchild == NULL){ //左子树为空
BiTree tempNode = root;
root = root->rchild;
free(tempNode);
}else if(root->rchild == NULL){ //右子树为空
BiTree tempNode = root;//临时指针
root = root->lchild;
free(tempNode);
}else{ //左右子树都不为空
//一般的删除策略是左子树的最大数据 或 右子树的最小数据 代替要删除的节点(这里采用查找左子树最大数据来代替)
BiTree tempNode = root->lchild;
if(tempNode->rchild!=NULL){
tempNode = tempNode->rchild;
}
root->key = tempNode->key;
DeleteNode(root->lchild,tempNode->key);
}
}
}
void InOrder(BiTree T)
{
if(T!=NULL)
{
InOrder(T->lchild);
printf("%3d",T->key);
InOrder(T->rchild);
}
}
//二叉排序树的创建,中序遍历,查找,删除
int main()
{
BiTree T; //树根
BiTree parent; //存储父亲结点的地址值
BiTree search;
KeyType str[]={54,20,66,40,28,79,58};//将要进入二叉排序树的元素值
Creat_BST(T,str,7);
InOrder(T);
printf("\n");
search=BST_Search(T,40,parent);
if(search)
{
printf("找到对应结点,值=%d\n",search->key);
}else{
printf("未找到对应结点\n");
}
DeleteNode(T,66);
InOrder(T);
printf("\n");
return 0;
}