数据结构代码题Day10
已知一颗二叉树采用链式存储结构,请设计一个算法,输出根节点到每个叶子结点的路径。(哈工大考试题)
题目分析
题目要求我们对二叉树的跟节点到叶子结点的路径进行打印,这里与上一篇的代码不同,上一篇是打印叶子结点到跟结点的路径
虽然题目不同,但是同样对于算法实现逻辑有些许相同。
- 题目转化例子图如下:
- 对于当前的路径打印实现方式,若要采用一个数据结构对其进行实现结点数据域的存储操作,我们这里优先选择栈结构进行设计,采用栈结构的初衷便是来源于二叉树的前序遍历的非递归的设计原理。
先来回顾下之前学习的二叉树前序遍历非递归代码实现~~~
二叉树中序遍历非递归代码
编写口诀:入栈向左一直走,出栈访问右子树
//二叉树前序遍历非递归
void PreOrderNo(BiTree * root){
if(root == NULL){//判断为NULL
return;
}
InitStack(S);//初始化栈
while(root != NULL && !isEmpty(S)){
//入栈向左一直走
if(root != NULL){
push(S,root);
root = root->lchild;
}else{
//出栈访问右子树
pop(S,root);
visit(root->data)
root = root->rchild;
}
}
}
在给出上述的非递归前序遍历的代码后,我们对其题目的代码编写进行设计。
- 设置一个栈;
- 从跟节点开始遍历,遍历后的结点全部压入栈中;
- 当走到叶子结点后停止,对其进行访问栈顺序存储栈----输出路径栈中的元素,
- 对左子树进行上述操作,
- 对右子树进行上述操作,
- 最重要的一步是–top
如何顺序访问栈?
为了便于访问栈内的元素,我们对其初始化一个顺序栈进行设计,原因在于可以通过0位置到top位置进行访问数据元素,其初始化顺序栈如下:
初始化顺序栈的代码如下:
int i = 0,top =0;
ElemType Stack[MAXSIZE];
算法执行过程分析
第一步
- 1、以先序遍历为主要实现方式
- 2、对根节点及其左子树的数据元素进行入栈操作
- 3、直到访问到叶子节点停止
先给出先序遍历的递归模板:
void PreOrder(BiTree *root){
if(root == NULL){
return ;
}
if(root != NULL){
//访问节点
visit(root->data);//访问节点
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
当然上述模板代码很容易写出来,下面根据上述的第一步执行进行改进:
- 初始化顺序栈
int i = 0,top =0;
ElemType Stack[MAXSIZE];
- 找到叶子结点
if(p->lchild == NULL && p->rchild == NULL){
}
- 数据访问入栈
if(p != NULL){
Stack[top] = p->data;//先赋值,这里的top初始化为0
++top;//进行top指针+1操作
}
进行代码的整合:
int i = 0,top =0;
ElemType Stack[MAXSIZE];
void path(BiTree * p){
if(p != NULL){//节点不为null
Stack[top] = p->data;//进行压栈
++top;
//在递归的过程中找到了叶子结点
if(p->lchild == NULL && p->rchild == NULL){
//待操作
break;//作为结果的出口
//这里我们只演示上图中1---2---4的执行过程
//因此在第一次访问到叶子节点后便暂时结束。
}
//visit(p->data);
path(p->lchild);//递归压栈左字树
path(p->rchild);//递归压栈左字树
}
}
注!!!
这里我们只演示上图中1—2—4的执行过程。因此在第一次访问到叶子节点后便暂时结束。
对于上述的代码执行如下图:
第二步、
- 在访问到叶子节点后,表示已经来到了我们需要的叶子节点的位置
- 在栈中存储着从根节点到叶子节点的路径
- 顺序访问栈;采用一个for循环
代码如下:
for(i = 0;i<top;i++){
//循环打印栈的位置,注意这里的i小于top,因为top当前所指的是栈顶,没有元素,看上图
printf("%s", Stack[i]);
}
将其添加到我们的第一步代码中:
int i = 0,top =0;
ElemType Stack[MAXSIZE];
void path(BiTree * p){
if(p != NULL){//节点不为null
Stack[top] = p->data;//进行压栈
++top;
//在递归的过程中找到了叶子结点
if(p->lchild == NULL && p->rchild == NULL){
for(i = 0;i<top;i++){
//循环打印栈的位置,注意这里的i小于top,因为top当前所指的是栈顶,没有元素,看上图
printf("%s", Stack[i]);
}
//出口
break;//只是用作停顿,类似于debug操作,这里不做要求,方便可以看懂代码。
}
//visit(p->data);
path(p->lchild);//递归压栈左字树
path(p->rchild);//递归压栈左字树
}
}
上述代码的出口位置,在这之前通过printf函数打印出来了,1—2–4因此第一次根节点到叶子节点的实现到这里就大功告成了!!
第三步、
- 实现其他的叶子节点的路径打印;
- 当前处于左子树的最左边的节点,栈的指针在栈顶节点。
- 因此我们需要进行退栈,只有退栈才可以回到父亲节点,并继续访问右子树。
这里要引出之前文章中如何进行向上走,链接如下:
子节点访问根节点的方式
看到这个图,便对父节点的访问有了清楚的认识,这里我们在叶子节点的位置,无法直接对父节点进行访问,但是通过这个
--top;//进行退回栈
通过上述的代码添加,说明在上图的3位置处,进行回退,结合这个规律,我们对其第二部的代码进行更深层次的改进。
int i = 0,top =0;
ElemType Stack[MAXSIZE];
void path(BiTree * p){
if(p != NULL){//节点不为null
Stack[top] = p->data;//进行压栈
++top;
//在递归的过程中找到了叶子结点
if(p->lchild == NULL && p->rchild == NULL){
for(i = 0;i<top;i++){
//循环打印栈的位置,注意这里的i小于top,因为top当前所指的是栈顶,没有元素,看上图
printf("%s", Stack[i]);
}
//出口
break;//只是用作停顿,类似于debug操作,这里不做要求,方便可以看懂代码。
}
//visit(p->data);
path(p->lchild);//递归压栈左字树
path(p->rchild);//递归压栈左字树
--top;//第三步改进,
//访问父节点
}
}
根据上述的代码对第二步的访问过程进行改进,得到:
因此最终的执行代码如下:
int i = 0,top =0;
ElemType Stack[MAXSIZE];
void path(BiTree * p){
if(p != NULL){
Stack[top] = p->data;//入栈
++top;
//找到叶子节点
if(p->lchild == NULL && p->rchild == NULL){
for(i = 0;i<top;i++){
printf("%s", Stack[i]);
}
}
//前序访问左右子树
path(p->lchild);
path(p->rchild);
//回到根节点
--top;
}
}
注:
个人代码问题或需要程序编写辅导服务等问题请加闲鱼【代码无bug】
或点击下面链接跳转闲鱼进行咨询