求二叉树上结点的路径

南昌航空大学实验报告

课程名称:   数据结构A   实验名称:  实验六   二叉树及其应用 (一)       

班   级:     XXX           学生姓名:      XXX         学号:     XXXXX      

指导教师评定:      XXX     签    名:      XXX    

一、实验目的

树形结构是一类重要的非线性数据结构,树中结点之间具有明确的层次关系。本实验是有关二叉树的存储结构及其遍历的应用。

二、实验内容

在实验中,设计一程序求二叉树上结点的路径。

三、程序分析

在二叉树上无论采用哪种遍历方法,都能够访问遍树中的所有结点。由于访问结点的顺序不同,前序遍历和中序遍历都很难达到设计的要求;但采用后序遍历二叉树是可行的,因为后序遍历是最后访问根结点,按这个顺序将访问过的结点存储到一个顺序栈中,然后再输出即可。因此我们可非递归地后序遍历二叉树bt,当后序遍历访问到结点*p时,此时栈stack中存放的所有结点均为给定结点*p的祖先,而由这些祖先便构成了一条从根结点到结点*p之间的路径。

为了加深对遍历二叉树的理解,在这里把实现二叉树的非递归遍历概念也加入到这个设计要求中。

本程序共分四个部分:

(1)二叉树建立;

(2)求指定结点路径;

(3)二叉树的前、中、后序非递归遍历算法;

(4)查找函数。

四、程序源代码

过程见后续,不想看过程的直接拉到底即可。

编写准备

看一下二叉树的定义

    二叉树(Binary tree)是n(n≥0)个结点的有限集合。若n=0时称为空树,否则:

有且只有一个特殊的称为树的根(Root)结点;

若n>1时,其余的结点被分成为二个互不相交的子集T1,T2,分别称之为左、右子树,并且左、右子树又都是二叉树。左右子树次序不能任意颠倒。

以及二叉树的存储结构顺序存储结构链式存储结构。

二叉树的遍历

定义:遍历二叉树(Traversing Binary Tree):是指按指定的规律对二叉树中的每个结点访问一次且仅访问一次。

常用的遍历方法有:

先序(DLR)----根、左子树、右子树;

中序(LDR)---左子树、根、右子树;

后序(LRD)---左子树、右子树、根;

层次遍历,从上到下、从左到右

并且对于二叉树的遍历有递归遍历算法和非递归遍历算法。

设计过程

定义部分

二叉树的存储结构分为顺序存储结构链式存储结构,这块采用链式存储结构以及使用常用的二叉链表结点定义。

二叉链表结点。有三个域:一个数据域,两个分别指向左右子结点的指针域。

typedef struct BTNode
{  ElemType  data ;
struct BTNode  *Lchild , *Rchild ;
}BTNode ,*BinTree;

创建二叉树部分

这里采用按先序遍历方式建立

BTNode *Create_BTree(BinTree &T)//建立链式二叉树,返回指向根结点的指针变量
{
    ElemType ch;
    ch=getchar();getchar();
    if(ch=='?')
    {
        T=NULL;
        return(T);
    }
    else
    {
        T=(BTNode *)malloc(sizeof(BTNode));
        T->data=ch;
        Create_BTree(T->Lchild);
        Create_BTree(T->Rchild);
        return(T);
    }
}

遍历部分

对于二叉树的遍历有递归遍历算法和非递归遍历算法,同时又分为先、中、后序遍历。

1.先序非递归遍历算法:先输出根节点,然后是左子树、右子树

void PreOrder_Traverse(BinTree t)//先序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			printf("%c",t->data);
			st[++top]=t;//入栈
			t=t->Lchild;
		}
		if(top!=-1)//取出一个右结点
		{
			t=st[top--];//top--是为了查询最左结点的右结点,而不是--top
			t=t->Rchild;
		}
	}
}

2.中序非递归遍历算法:先输出左子树,然后是根节点、右子树

void InOrder_Traverse(BinTree t)//中序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			st[++top]=t;//入栈
			t=t->Lchild;
		}
		if(top!=-1)//取出一个右结点
		{

			t=st[top--];//top--是为了查询最左结点的右结点
			printf("%c",t->data);
			t=t->Rchild;
		}
	}
}

3.后序非递归遍历算法:先输出左子树,然后是右子树、根节点

这一部分实现不同于前面两个。

先分析一下先序为DLR,后序为LRD

将先序遍历的左右结点颠倒,则得到DRL

再将DRL进行一个颠倒(即一轮入栈和出栈)即为LRD,从而实现后序非递归遍历算法

//先序:DLR,后序:LRD
//左右结点颠倒的先序遍历:DRL
//DRL进行一个颠倒(即一轮入栈和出栈)即为LRD,从而实现后序非递归遍历算法
void PostOrder_Traverse(BinTree t)//先序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	ElemType s[MAX_SIZE];//建立顺序栈,进行压栈与出栈行为
	int top1=0;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			s[top1++]=t->data;//先序时为(printf("%c",t->data);),这里将其进行压栈
			st[++top]=t;//入栈
			t=t->Rchild;//先序时为(t=t->Lchild;),这里进行左右结点颠倒
		}
		if(top!=-1)//取出一个右结点
		{
			t=st[top--];//top--是为了查询最左结点的右结点
			t=t->Lchild;//先序时为(t=t->Rchild;),这里进行左右结点颠倒
		}
	}
	while(top1--)printf("%c",s[top1]);//进行出栈,完成颠倒
}

求指定结点路径部分

鉴于上一部分遍历的设计,以及要求中的“一条从根结点到结点*p之间的路径”这块选择最为容易且最合适的先序遍历算法(先输出根节点,然后是左子树、右子树)。

void Find_path(BinTree t,ElemType c)//求指定结点路径
{
	static ElemType path[MAX_SIZE];
	static int i=0;
	if(!t) return; //当前访问的结点为空,直接返回
    if(t->data==c)//已找到目标
    {
        printf("路径为:");
        for(int j=0;j<i;j++)printf("%c->",path[j]);//输出目标结点的祖先
        printf("%c\n",t->data);//输出目标结点
        return;
    }
    else
    {
        path[i++]=t->data;
        Find_path(t->Lchild,c);
        Find_path(t->Rchild,c);//利用先序遍历算法来查找路径,从根结点开始查,仅查祖先
        i--;
    }
}

源代码:

#include<stdio.h>
#include<stdlib.h>
#define ElemType char
#define MAX_SIZE 100
typedef struct BTNode
{
    ElemType data;//结点数据
    struct BTNode *Lchild;//左子结点指针
    struct BTNode *Rchild;//右子结点指针
}BTNode,*BinTree;

BTNode *Create_BTree(BinTree &T)//建立链式二叉树,返回指向根结点的指针变量
{
    ElemType ch;
    ch=getchar();getchar();
    if(ch=='?')
    {
        T=NULL;
        return(T);
    }
    else
    {
        T=(BTNode *)malloc(sizeof(BTNode));
        T->data=ch;
        Create_BTree(T->Lchild);
        Create_BTree(T->Rchild);
        return(T);
    }
}

void PreOrder_Traverse(BinTree t)//先序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			printf("%c",t->data);
			st[++top]=t;//入栈
			t=t->Lchild;
		}
		if(top!=-1)//取出一个右结点
		{
			t=st[top--];//top--是为了查询最左结点的右结点,而不是--top
			t=t->Rchild;
		}
	}
}

void InOrder_Traverse(BinTree t)//中序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			st[++top]=t;//入栈
			t=t->Lchild;
		}
		if(top!=-1)//取出一个右结点
		{

			t=st[top--];//top--是为了查询最左结点的右结点
			printf("%c",t->data);
			t=t->Rchild;
		}
	}
}

//先序:DLR,后序:LRD
//左右结点颠倒的先序遍历:DRL
//DRL进行一个颠倒(即一轮入栈和出栈)即为LRD,从而实现后序非递归遍历算法
void PostOrder_Traverse(BinTree t)//先序非递归遍历算法
{
	BinTree st[MAX_SIZE];//声明一个栈数组,用于实现LIFO结点调用
	int top=-1;
	ElemType s[MAX_SIZE];//建立顺序栈,进行压栈与出栈行为
	int top1=0;
	while(t||top!=-1)
    {
		while(t)//一路向左
		{
			s[top1++]=t->data;//先序时为(printf("%c",t->data);),这里将其进行压栈
			st[++top]=t;//入栈
			t=t->Rchild;//先序时为(t=t->Lchild;),这里进行左右结点颠倒
		}
		if(top!=-1)//取出一个右结点
		{
			t=st[top--];//top--是为了查询最左结点的右结点
			t=t->Lchild;//先序时为(t=t->Rchild;),这里进行左右结点颠倒
		}
	}
	while(top1--)printf("%c",s[top1]);//进行出栈,完成颠倒
}


void Find_path(BinTree t,ElemType c)//求指定结点路径
{
	static ElemType path[MAX_SIZE];
	static int i=0;
	if(!t) return; //当前访问的结点为空,直接返回
    if(t->data==c)//已找到目标
    {
        printf("路径为:");
        for(int j=0;j<i;j++)printf("%c->",path[j]);//输出目标结点的祖先
        printf("%c\n",t->data);//输出目标结点
        return;
    }
    else
    {
        path[i++]=t->data;
        Find_path(t->Lchild,c);
        Find_path(t->Rchild,c);//利用先序遍历算法来查找路径,从根结点开始查,仅查祖先
        i--;
    }
}

int main()
{
    BinTree t=NULL;//根节点指针
    ElemType ch;
    printf("按照先序遍历输入二叉树:");
    t=Create_BTree(t);

    printf("先序遍历输出:");
    PreOrder_Traverse(t);printf("\n");
    printf("中序遍历输出:");
    InOrder_Traverse(t);printf("\n");
    printf("后序遍历输出:");
    PostOrder_Traverse(t);printf("\n");

    while(1)
    {
        printf("输入要查询路径的结点:");
        scanf("%c",&ch);getchar();
        Find_path(t,ch);printf("\n");
    }
    return 0;
}

运行结果

以下图为例,输入的字符序列应当是:ABD??E?G??CF???

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值