王道数据结构代码题--第二章树

第二章 二叉树

顺序存储结构的二叉树,设计一个算法求,编号为i和j的两个结点的最近公共祖先结点的值
1、二叉树顺序顺序存储
2、最近公共祖先结点,

1、双亲编号问题,i的祖先结点为i/2,j的祖先结点为j/2
2、取多少次
3、将较大的不断取一半,比较i和j的大小,不断将大的取一半

二叉树:
1、顺序存储:
适用范围:完全二叉树、满二叉树

2、链式存储

对于普通的二叉树如何使用顺序存储?
在存储的过程中将逻辑关系表达出来,将普通二叉树转换为完全二叉树,用数组按照层次一层一层保留,在存储过程中下标从1开始。

算法思想:不断的将i、j较大的取一半,比较i、j的大小,直到i=j,找到公共祖先。

int com_Ancestor(SqTree T,int i,j)
{
	if(T[i ] ! = NULL && T[j] != NULL)//如果第i个结点不为空
	{		while(i != j)//如果i不等于j则说明没有找到
			{
					if(i > j)//如果i大于j则i取一半,i往前走
					{
							i = i/2;
					}
					else//否则j大,取一半
					{
							j = j/2;
					}
			}
	}
	return T[i};
}

二叉树的遍历(递归),链式存储

typedef struct BiTNode
{
	ElemType data;
	struct BiTNode *lchild,*rchild;
}BiTNode,*BiTNode

每个结点都访问一次,且只访问一次,时间复杂度为O(n),在最坏情况下,二叉树是有n个结点且深度为n的单只树

void preorder(BiTree T)
{
	if(T != NULL)
	{
		visit(T);
		preorder(T->lchild);
		preorder(T->rchild);
	}
}
void Inorder(BiTree T)
{
	if(T != NULL)
	{
		Inorder(T->lchild);
		visit(T);
		Inorder(T->rchild);
	}
}
void Postorder(BiTree T)
{
	if(T != NULL)
	{
		Postorder(T->lchild);
		Postorder(T->rchild);
		visit(T);
	}
}

二叉树的中序遍历(非递归)

中序遍历:左根右
1、沿着根的左孩子依次入栈,直到左子树为空
2、栈顶元素出栈,并访问,若出栈的结点右孩子为空则继续出栈
3、否则执行1

口诀:入栈向左一直走,出栈访问右子树

//中序遍历非递归
void Inorder(BiTree T)
{
		InitStack(s);
		BiTree p  = T;
		while(p != NULL || !IsEmpty(s))//当两个都不为空的时候才执行以下操作,p结点不为空且栈不为空
		{
				if(p != NULL)
				{
						push(s,p);
						p = p->lchild;//一路往左走
				}
				else
				{
						pop(s,p);//栈顶元素出栈并访问
						visit(p);
						p = p->rchild;
				}
		}
}

二叉树的先序遍历(非递归)
前序遍历:根左右

//后序遍历非递归
void Inorder(BiTree T)
{
		InitStack(s);
		BiTree p  = T;
		while(p != NULL || !IsEmpty(s))//当两个都不为空的时候才执行以下操作,p结点不为空且栈不为空
		{
				if(p != NULL)
				{
						push(s,p);
						visit(p);
						p = p->lchild;//一路往左走
				}
				else
				{
						pop(s,p);//栈顶元素出栈并访问
						p = p->rchild;
				}
		}
}

二叉树的后序遍历(非递归)
后续遍历:左右根

算法思想:
1、沿着根的左孩子依次入栈,直到左孩子为空
2、读栈顶元素,判断,
若右孩子不为空且没有被访问过,将右子树执行1
3、若右子树为空则或被访问过,栈顶元素出栈

口诀:入栈向左一直走,判定(右子树),出栈访问标记重置

void Postorder(BiTree T)
{
		InitStack(s);
		BiNode *p = T;
		BiTNode r = NULL;//标记最近访问过的结点
		if(p != NULL)//如果结点不为空则一直往左走,否则取栈顶元素
		{
				push(s,p);
				p = p->lchild;//一直处理左子树
		}
		else
		{
				GetTop(s,p);//读栈顶元素
				if(p->rchild && p->rchild != r)//如果右孩子不为空且没有被访问过,则处理右子树,否则将结点出栈并且标记被访问过
				{
						p = p->rchild;//先处理右子树
						push(s,p);
						p  = p->lchild;
						
				}
				else
				{
						pop(s,p);//若被访问过或为空则直接出栈,并且访问
						visit(p->data);
						r = p;//标记被访问过
						p = NULL;
				}
		}
}

二叉树的层次遍历
如果有左子树,则入队
如果有右子树,则入队
算法思想:将根结点入队,出队,访问出队结点,若它有左子树,则将左子树入队,如果它有右子树,则将右子树入队,反复进行直到为空。

入队出队访问,有左入左,有右入右

void leveorder(BiTree T)
{
		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->rlchild);
				}
		}
}

二叉树自上而下,从右到左的层次遍历算法
1、队列出队元素入栈
算法思想:层次遍历利用原本层次遍历算法出队的同时入栈,再次出栈。

void leverorder(BiTree T)
{
	InitQueue(Q);
	InitStack(S);
	BiTree p;
	if(T != NULL)//如果二叉树不为空
	{
		EnQueue(Q,T);
		while(!IsEmpty(Q))
		{
				DeQueue(Q,p);
				//visit(p);
				push(S,p);//不进行访问入栈
				if(p->lchild != NULL)
				{
						EnQueue(Q,p->lchild);
				}
				if(p->rchild != NULL)
				{
					  EnQueue(Q,p->rchild);
				}
		}
	}
	while(!IsEmpty(S))
	{
			pop(S,p);//出栈
			visitd(p);
	}
}

求二叉树的高度递归和非递归算法
算法思想:递归的求左子树高度,右子树高度取较大的+1

int Btdepth(BiTree T)
{
	if(T == NULL)
	{
			return 0;
	}
	
	ldep = Btdepth(T->lchild);//求左高度
	rdep = Btdepth(T->rchild);//求右高度
	
	if(ldep > rdep)//如果左子树大则左+1
	{
			return ldep+1;
	}
	else
	{
			return rdep+1;
	}
	//return (ldep > rdep:ldep?rdep)+1;
}

算法思想:last指向最右结点,当指向最右结点的时候level加1

如何让last始终在最后一个结点呢?

int Btdepth(BiTree T)
{
		if(T == NULL)
		{
				return 0;
		}
		int front = -1,rear = -1;
		int last = 0;
		int level = 0;
		BiTree Q[MaxSize];
		Q[++rear] = T;
		BiTree p;
		
		while(front < rear)//队列不空
		{
				p = Q[++front];//出队
				if(p->lchild != NULL)
				{
						Q[++rear] = p->lchild;//入队
				}
				if(p->rchild != NULL)
				{
						Q[++rear] = p->rchild;
				}
				if(front == last)//相当于rear每走两步,front走一步,front==last时,last等于rear,层数+1
				{
						level++;
						last = rear;//last指向最右结点
				}
		}
		return level+1
}

交换二叉树左右子树
算法思想:递归的交换左右子树,先交换根结点的左右子树,然后交换根的

void swap(BiTree T)
{	
		BiNode *p = T;
		int temp=;
		if(p != NULL)
		{
				swap(p->lchild);//递归的处理左子树
				swap(p->rchild);//递归的处理右子树
				temp = p->lchild;
				p->lchild = p->rchild;
				p->rchild = temp;
		}
}

求二叉树双分支(度为2)结点的个数
算法思想:

int DoubleNode(BiTree T)
{
		BiNode *p = T;
		if( p == NULL)//如果树为空
				return 0;
		else if(p->lchild != NULL && p->rchild != NULL)//是双分支结点
				return (DoubleNode(p->lchild) + DoubleNode(p->rchild)) + 1;//本身结点加1
		else
				return DoubleNode(p->lchild) + DoubleNode(p->rchild;//如果不是双分支,则等于左子树+右子树
}

链表双分支左右结点都不为空,
求二叉树中度为1的结点个数

int Node(BiTree T)
{
	BiNode *p = T;
	if(p == NULL)
			return 0;
	//else if(p->lchild != NULL || p->rchild != NULL)
	else if((p->lchild == NULL && p->rchild != NULL) || (p->lchild != NULL && p->rchild ==NULL))
			return (Node(p->lchild) + Node(p->rchild)) + 1;//+1所因为本身也属于
	else
			return Node(p->lchild) + Node(p->rchild;
}

求二叉树中度为0的结点个数

int Node(BiTree T)
{
	BiNode *p = T;
	if(p == NULL)
			return 0;
	else if(p->lchild == NULL && p->rchild == NULL)
			return (Node(p->lchild) + Node(p->rchild)) + 1;//+1所因为本身也属于
	else
			return Node(p->lchild) + Node(p->rchild;
}

求二叉树中叶子结点的个数
算法思想:递归法

int CountNode(BiTree T)
{
		BiNode *p = T;
		if( p == NULL)//递归边界
				return 0;
		if((p->lchild == NULL) && (p->rchild == NULL))//左子树为空且右子树为空
				return 1;
		n1 = CountNode(p->lchild);//递归式
		n2 = CountNode(p->rchild);
		
		return n1+n2;
}

算法思想:遍历法,如果是叶子结点则保留+1,不是叶子结点继续遍历

int CountNode(BiTree T)
{
		BiTNode *p = T;
		if(p != NULL)
		{
				if((p->lchild == NULL) && (p->rchild == NULL))
					++n;
				CountNode(p->lchild);
				CountNode(p->rchild);
		}
		return n;
}

求二叉树中所有结点的个数

int CountNoude(BiTree T)
{
		BiTNode *p = T;
		int n1 = 0,n2 = 0;
		if( p == NULL)
				return 0;
		else
		{
				n1 = CountNode(p->lchild);//递归求左子树
				n2 = CountNode(p->rchild);//递归求右子树
		}
		return n1+n2+1;//左+右+根
}

算法思想:在先序遍历访问时visited,计数+1

int count(BiTree T)
{	
		int n;
		BiTNode *p = T;
		if(T != NULL)
		{
				n++;
				count(p->lchild);
				count(p->rchild);
		}
}

求先序遍历中第k的结点的值
算法思想:先序遍历二叉树,求第k的结点的值

void preorder(BiTree T,int k)
{
		int n;
		BiTree *p = T;
		if( p != NULL)
		{
				//visit(p);
				++n;
				if(n == k)//判断是第k个结点
				{
						printf("%d",p->data);
				}
				preorder(p->lchild);
				preorder(p->rchild);
		}
}

寻找data域中等于key的结点是否存在,若存在将q指向它,否则q为空
算法思想:遍历过程中,判断结点是都等于key,如果等于则q指向它,否则为空

void preorder(BiTree *T,int key,BiTNode *q)
{
		BiTNode *p = T;
		if( p != NULL)
		{
				if(p->data ==key)
				{
						q = p;
				}
				//visit(p);
				preorder(p->lchild);
				preorder(p->rchild);
		}
}

将二叉树的叶子利用结点的右孩子指针(说明是二叉树链表结点),从左向右链接成一个单链表(head指向第一个,tail指向最后一个)
算法思想:递归的处理左子树和右子树,找到叶子结点,连城单链表,第一个结点的操作和剩余操作不一样

vold preorder(BiTree *p,BiTNode *head,BiTNode *taill)
{
		if(p != NULL)
		{
				if( p->lchild == NULL && p->rchild == NULL)
				{
						if(head == NULL)//如果head说明为第一个叶子结点
						{
							head = p;//head指向p
							tail = p;//tail指向p
						}
						else//如果head不为空只需要移动taill即可,因为head始终指向第一个结点
						{
							tail->rchild = p;//将tail的rchild指向p
							tail = p;//将tail指向p,因为taill始终指向最后一个结点
						}
				}
		}
		//visit(p);//if部分相当于访问结点 
		preorder(p->lchild,head,taill);
		preorder(p->rchild,head,taill);
}

求指定结点s的二叉排序树的层次
算法思想:根据二叉排序树的性质,左边的比根结点小,右边的比根结点大,查找结点,查找到就+1。

如果我们查找一个结点,值k大于根结点T则去左边找,如果k比当前根结点T大,则去右边找,因为根结点的右边都是大于T的值。

void BST_Search(BSTree *T,int k)//二叉排序树的查找
{
		if(! T)
				return ;
		while (T != NULL)
		{
				if(T->data == K)
				{
							printf("%d",T->data);
							break;
				}
				else if(T->data > K)
				{
							T = T->lchild;//如果结点的值大于k,则说明k比结点小去左子树找,因为左边的值都小于T。
				}
				else
				{
							T = T->rchild;//如果结点的值小于k,则取左子树找
				}
		}
}
int level(BiTree T)
{
		BiTNode *p = T;
		int n = 0;
		if(T != NULL)
		{
				while(p->data != s->data)
				{
						if(p->data < s->data)//比s大去右边找,比s小去左边找
						{
								p = p->rchild;
						}
						else
						{
								p = p->lchild;
						}
						++n;//找到结点+1
				}
		}
		return n;
}

求二叉树的WPL
WPL=叶子结点*深度
算法思想:递归,将所有叶子结点的WPL值累加
1、找叶子结点
2、

int WPL_preorder(BiTree root,int deep)
{
		BiTNode *p = root;
		int WPL = 0;
		if(root != NULL)
		{
				//visited(P);
				if((root->lchild == NULL) &&(root->rchild))//如果是叶子结点则计算WPL
				{
						WPL += deep*root->weight;//叶子结点的权重*深度
				}
				if(root->lchild != NULL)//如果左子树不空则求左子树
						WPL_preorder(root->lchild,deep+1)
				
				if(root->rchild != NULL)//如果右子树不空则求右子树
						WPL_preorder(root->rchild,deep+1)
				
				//preorder(p->lchild);
				//preorder(p->rchild);	
			}
	return WPL;
}
int WPL(BiTree root)
{
			return WPL_preorder(root,0);//deep从0开始递归遍历求WPL
}

将给定表达式转换为等价的中缀表达式,通过括号反映计算次序

算法思想:
括号如何加 ?
1、根不加括号
2、叶子结点不加括号
3、其他结点加括号
中序访问左子树之前加
中序访问右子树之后加

void Inorder(BiTree *root,int deep)
{
		if(root == NULL)//如果是空则 直接返回
		{
					return ;
		}
		else if(root->lchild == NULL && root->rchild == NULL)//如果是叶子结点,输出操作数,不加括号
		{
				printf();//不需要加括号
		}
		else
		{
				if(deep > 1)//若有子表达式,则加一层括号,中序访问之前加
						printf("(");
				Inorder(root->lchild,deep+1);
				printf("%s",root->data);
				Inorder(root->right,deep+1);
				if(deep > 1)//若有子表达式,则加一层括号,中序访问之后加
						printf(")")
		}
}
void fun(BiTree *root)
{
		Inorder(root,1);//根的高度为1
}

求二叉树中值为x的层号

int fun(BiTNode *p,int x)
{	
			int h = 1;
			if(p != NULL)
			{
					if(p->data == x)
							printf(h)
					++h;//当第一次访问结点h+1
					fun(p->lchild,x);
					fun(p->rchild,x);
					--h;//第三次访问结点h-1
			}
			return h;
}

增加一个指向双亲结点的parent,并指向指针赋值,并输出所有结点到根结点的路径
手动模拟
1、为什么增加parent指针
2、单个结点到根结点路径
3、遍历整棵树

//结构体
typedef struct BiTNode
{
		char data;
		struct BiTNode *parent,lchild,rchild;
}BITNode;
//增加parten指针,指向p结点的父结点
void fun(BiTNode *p,BiTNode *q)
{
			if(p != NULL)//增加一个parent指针
			{
					p->parent = q;
					q = p;
					fun(p->lchild,q);
					fun(p->rchild,q);
			}
}
//单个结点输出路径 
void print(BiTNode *p)
{
		while(p != NULL)
		{
				printf();
				p = p->parent;//通过partent指针实现输出结点
		}
}
//输出所有结点的路径
void allprint(BiTree *T)
{
		if(p != NULL)
		{
					print(p);
					allprint(T->lchild);
					allprint(T->rchild);
		}
}

输出根结点到叶子结点的路径
1、遍历过程中碰到叶子结点开始处理(打印出来)
2、对特殊结点进行处理
3、借助一个栈

int path(BiTNode *p)
{
		int i = 0,top = 0;
		Elemtype stack[MAXSIZE];
		if(p != NULL)//p不为空则入栈
		{
				stack[++top] = p->data;//入栈
		}
		if(p->lchild == NULL && p->rchild == NULL)//叶子结点则输出栈中元素
		{
					for(i = 0;i < top;i++)//输出栈中元素
					{
								printf(stack[i]);
					}
		}
		path(p->lchild);//遍历
		path(p->rchild);
		--top;//当入栈时++top,退栈--top

}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值