第四章.树与二叉树基本算法

一.二叉树的存储结构

1. 顺序存储结构,最适合于完全二叉树,用于存储一般二叉树会浪费大量存储空间
2.链式存储结构

typedef struct BTNode
{
	char data;
	struct BTNode *lchild;
	struct BTNode *rchild;
}BTNode;

二.二叉树的遍历算法

1.先序遍历(递归以及非递归)

void preOrder(BTNode *p)
{// 根--左--右
	if(p != NULL)
	{
		Visit(p); //假设访问函数Visit()已经定义过,包含对结点p的各种访问操作
		preOrder(p->lchild); //先序遍历左子树
		preOrder(p->rchild); //先序遍历右子树
	}
}
void preOrderNonRecursion(BTNode *bt)
{
	if(bt != NULL)
	{
		BTNode * Stack[maxSize]; //定义一个栈
		int top = -1; //初始化栈
		BTNode *p;
		Stack[++top] = bt;
		while(top != -1)
		{
			p = Stack[top--]; //出栈并输出栈顶结点
			Visit(p);
			//先右后左
			if(p->rchild != NULL) //栈顶结点的右孩子存在,则右孩子入栈
				Stack[++top] = p->rchild;
			if(p->lchild != NULL) //栈顶结点的左孩子存在,则左孩子入栈
				Stack[++top] = p->lchild;
		}
	}
}

2.中序遍历(递归以及非递归)

void inOrder(BTNode *p)
{// 左--根--右
	if(p != NULL)
	{
		inOrder(p->lchild);
		Visit(p);
		inOrder(p->rchild);
	}
}
void inOrderNonRecursion(BTNode *bt)
{ //循环执行如下操作:如果栈顶结点左孩子存在,则左孩子入栈;
  //如果栈顶结点左孩子不存在,则出栈并输出栈顶结点,
  //然后检查其右孩子是否存在,如果存在,则右孩子入栈
 	if(bt != NULL)
 	{
 		BTNode *Stack[maxSize];
 		int top = -1;
 		BTNode *p;
 		p = bt;
 		while(top != NULL || p!= NULL)
 		{
 			while(p != NULL) //左孩子存在,则左孩子入栈
 			{
 				Stack[++top] = p;
 				p = p->lchild;
 			}
 			if(top != -1) //在栈不空的情况下出栈并输出栈结点
 			{
 				p = Stack[top--];
 				Visit(p);
 				p = p->rchild;
 			}
 		}
 	}
	
}

3.后序遍历(递归以及非递归)

void postOrder(BTNode *p)
{ //左--右--根
	if(p != NULL)
	{
		postOrder(p->lchild);
		postOrder(p->rchild);
		Visit(p);
	}
}
void postOrderNonRecursion(BTNode *bt)
{
	if(bt != NULL)
	{
		//定义两个栈
		BTNode *Stack1[maxSize];
		int top1 = -1;
		BTNode *Stack2[maxSize];
		int top2 = -1;
		BTNode *p = NULL;
		Stack1[++top1] = bt;
		while(top1 != -1)
		{
			p = Stack1[top1--];
			Stack2[++top2] = p; //注意这里与先序遍历区别,输出改为入Stack2
			//先左后右
			if(p->lchild != NULL)
				Stack1[++top1] = p->lchild;
			if(p->rchild != NULL)
				Stack1[++top1] = p->rchild;
		}
		while(top2 != -1)
		{
			//出栈序列即为后序遍历序列
			p = Stack2[top2--];
			Visit(p);
		}
	}
}

3.
在这里插入图片描述

int comp(BTNode *p)
{
	int A,B;
	if(p != NULL)
	{
		//如果当前结点的左子树和右子树非空,则为表达式,用后序遍历求值
		if(p->lchild != NULL && p->rchild != NULL)
		{ 
			A = comp(p->lchild); //后序遍历求出左子树的值,赋值给A
			B = comp(p->rchild); //后序遍历求出右子树的值,赋值给B
			return op(A,B,p->data); //根据已求得的A与B和当前结点的运算符求出整个表达式的值
		}
		else
			return p->data-'0'; //如果当前结点的左右子树都为空,则为数值,直接返回p->data-'0'是将字符型数字转化为整型数字
	}
	else
		return 0; //如果是空树,则表达式的值为0
}
//函数int op(int A,int B,char C)返回的是以C为运算符,以A,B为操作数的算式的值

4.
在这里插入图片描述

int getDepth(BTNode *p)
{
	int LD,RD;
	if(p == NULL)
		return 0; //如果是空树则返回0,定义空树的深度为0
	else
	{
		LD = getDepth(p->lchild); //求左子树的深度
		RD = getDepth(p->rchild); //求右子树的深度
		return (LD>RD? LD:RD)+1; //返回左右子树深度的最大值+1
	}
}

5.
在这里插入图片描述

//假设二叉树已经存在并且p指向其根节点
void search(BTNode *p,BTNode *&q,int key)
{
	if(p != NULL) //如果树为空树,则什么都不做,q保持NULL值
	{
		if(p->data == key)
			q = p;
		else
		{
			search(p->lchild,q,key);
			search(p->rchild,q,key);
		}
	}
}
//剪枝操作
void search(BTNode *p,BTNode *&q,int key)
{
	if(p != NULL)
	{
		if(p->data == key)
			q = p;
		else
		{
			search(p->lchild,q,key);
			if(q == NULL) //在左子树中没找到才到右子树中查找
				search(p->rchild,q,key);
		}
	}
}

6.
在这里插入图片描述

int n = 0; //定义全局变量n,将结点计数初值设为0
void trave(BTNode *p,int k)
{ //前序
	if(p != NULL)
	{
		++n; //当第一次来到一个结点的时候进行计数,表示这是第n个结点
		if(k == n) //当当第一次来到一个结点的时候进行判断,看这个结点是不是先序序列中的第k个结点
		{
			cout<<p->data<<endl; //如果是,则输出
			return; //并且无需继续遍历,用return直接返回
		}
		trave(p->lchild,k);
		trave(p->rchild,k);
	}
}
void trave(BTNode *p,int k)
{ //中序
	if(p != NULL)
	{
		trave(p->lchild,k);
		++n;
		if(k == n)
		{
			cout<<p->data<<endl;
			return;
		}
		trave(p->rchild,k);
	}
}
void trave(BTNode *p,int k)
{//后序
	if(p != NULL)
	{
		trave(p->lchild,k);
		trave(p->rchild,k);
		++n;
		if(k == n)
		{
			cout<<p->data<<endl;
			return;
		}
	}
}

7.层次遍历(需要一个循环队列)

void level(BTNode *p)
{
	int front,rear;
	BTNode *que[maxSize]; //定义一个循环队列,用来记录将要访问的层次上的结点
	front = rear = 0;
	BTNode *q;
	if(p != NULL)
	{
		rear = (rear+1)%maxSize;
		que[rear] = p; //根节点入队
		while(front != rear) //当循环队列不空的时候进行循环
		{
			front = (front+1)%maxSize;
			q = que[front]; //队头结点出队
			Visit(q); //访问队头结点
			if(q->lchild != NULL) //如果左子树不空,则左子树的根结点入队
			{
				rear = (rear+1)%maxSize;
				que[rear] = q->lchild;
			}
			if(q->rchild != NULL) //如果右子树不空,则右子树的根结点入队
			{
				rear = (rear+1)%maxSize;
				que[rear] = q->rchild;
			}
		}
	}
}

8.
在这里插入图片描述
在这里插入图片描述

//下面所定义的这个结构类型为顺序非循环队列的队列元素,可以存储结点指针以及结点所在的层次号
typedef struct
{
	BTNode *p; //结点指针
	int lno;
}St;
int maxNode(BTNode *b)
{
	St que[maxSize];
	int front,rear; //定义顺序非循环队列
	int Lno,i,j,n,max;
	front = rear = 0;
	BTNode *q;
	if(b != NULL)
	{
		++rear;
		que[rear].p = b; //树根入队
		que[rear].lno = 0; //树根所咋层次号设置为1,此为已知条件
		while(front != rear)
		{
			++front;
			q = que[front].p;
			Lno = que[front].lno; //关键语句:Lno用来存取当前结点的层次号
			if(q->lchild != NULL)
			{
				++rear;
				que[rear].p = q->lchild;
				que[rear].lno = Lno + 1; 
			}
			if(q->rchild != NULL)
			{
				++rear;
				que[rear].p = q->rchild;
				que[rear].lno = Lno + 1;
			}
		}//注意:循环结束后Lno保存的是这颗二叉树的最大层数
		max = 0for(i=1;i<=Lno;++i)
		{
			n = 0;
			for(j=1;j<=rear;++j) //rear指向队尾元素,并且队列数组中que[1]为树的根节点
				if(que[j].lno == i)
					++n;
				if(max<n)
					max = n;
		}
		return max;
	}
	else return 0; //空树直接返回0
}

三.线索二叉树

1.线索二叉树结点定义

typedef struct TBTNode
{
	char data;
	int ltag,rtag; //线索标记
	struct TBTNode *lchild;
	struct TBTNode *rchild;
}TBTNode;

2.中序线索二叉树

void InThread(TBTNode *p,TBTNode *&pre)
{
	if(p != NULL)
	{
		InThread(p->lchild,pre); //递归,左子树线索化
		if(p->lchild == NULL)
		{						//建立当前结点的前驱线索
			p->lchild = pre;
			p->ltag = 1;
		}
		if(pre != NULL && pre->rchild == NULL)
		{						//建立前驱结点的后继结点
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p; //pre指向当前的p,作为p将要指向的下一个结点的前驱结点
		InThread(p->rchild,pre); //递归,右子树线索化
	}
}
void createInThread(TBTNode *root)
{
	TBTNode *pre = NULL;
	if(root != NULL)
	{
		InThread(root,pre);
		pre->rchild = NULL; //非空二叉树,线索化后处理中序最后一个结点
		pre->rtag = 1;
	}
}

2.遍历中序线索二叉树

TBTNode *First(TBTNode *p)
{
	while(p->ltag == 0)
		p=p->lchild; //最左下结点(不一定是叶子结点)
	return p;
}
TBTNode *Last(TBTNode *p)
{
	while(p->rtag == 0)
		p=p->rchild; //最右下结点
	return p;
}
TBTNode *Next(TBTNpde *p)
{
	if(p->rtag == 0)
		return First(p->rchild);
	else
		return p->rchild;
}
TBTNode *Prior(TBTNode *p)
{
	if(p->lchild == 0)
		return Last(p->lchild);
	else
		return p->lchild;
}
void InOrder(TBTNode *root)
{
	for(TBTNode *p=First(root);p!=NULL;p=Next(p))
		Visit(p);
}

4.前序线索二叉树

void preThread(TBTNode *p,TBTNode *&pre)
{
	if(p != NULL)
	{
		if(p->lchild == NULL)
		{
			p->lchild = pre;
			p->ltag = 1;
		}
		if(pre != NULL && pre->rchild == NULL)
		{
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p;
		//注意:这里在递归入口处有限制条件,左右指针不是线索才能继续递归
		if(p->ltag == 0)
			preThread(p->lchild,pre);
		if(p->rtag == 0)
			preThread(p->rchild,pre);
	}
}

5.前序线索二叉树的遍历

void preOrder(TBTNode *root)
{
	if(root != NULL)
	{
		TBTNode *p = root;
		while(p != NULL)
		{
			while(p->ltag == 0) //左指针不是线索,则边访问边左移
			{
				Visit(p);
				p = p->lchild;
			}
			Visit(p); //此时p左指针必为线索,但还没有访问,则访问
			p = p->rchild; //此时p左孩子不存在,则右指针若非空,
						   //则不论是否为线索都指向其后继
		}
	}
}

6.后序线索二叉树

void postThread(TBTNode *p,TBTNode *&pre)
{
	if(p != NULL)
	{
		postThread(p->lchild,pre); //递归,左子树线索化
		postThread(p->rchild,pre); //递归,右子树线索化
		if(p->lchild == NULL)
		{
			p->lchild = pre;
			p->ltag = 1;
		}
		if(pre != NULL && pre->rchild == NULL)
		{
			pre->rchild = p;
			pre->rtag = 1;
		}
		pre = p;
	}
}

四.二叉排序树

1.二叉排序树的存储结构

typedef struct BTNode
{
	int key; //这里将data改为key,代表关键字
	struct BTNode *lchild;
	struct BTNode *rchild;
}BTNode;

2.二叉排序树查找关键字算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值