树学习关键点

最近研究树,写代码的时候总有一种很不爽的感觉,主要是因为递归调用不太直观,我在《递归调用》中说递归调用的学习需要经历三个阶段。但是即便我已经能够用树来理解递归,但真正写树的递归的时候还是会有很不好的感觉。

昨天一直在想这个问题,如何去除这样一种不好的感觉,也就是如何能把递归调用想的再通透一点,到了晚上终于有点眉目。今天就拿树的例子来说明。

在《链表学习关键点》中说到了一种指向结点的指针的移动方式,就是通过trv = trv->next这种赋值的方式,另外一种呢,就是通过函数调用中实参给形参传值的方式来移动指针。举个例子:

要求,首先得知道树长什么样子。清楚树大多是通过递归调用来创建和遍历。

假设已经建成了如下的树

写了如下程序:

typedef struct Tree
{
	char data;
	struct Tree*lchild,*rchild;
}BiTree;

void fn(BiTree *root)
{

}

int main()
{
	BiTree * root;			//指向根结点的指针
	CreateBiTree(&root);	<span style="white-space:pre">	</span>//假设通过该函数构建了一颗如上如图所示的树
	fn(root->lchild);
	return 0;
}

当调用fn函数的时候,传递的是root->lchild,那么在fn这个函数中的形参root拿到的值是不是如下图所示(下面的那个root):



有没有发现其实这样的方式和root = root->lchild 是一样。

那么为什么图中会有两个root呢?这就是栈的作用了,使用栈主要就是因为栈具有记忆功能(可以实现不用递归遍历树,也会用到栈)

写一个前序遍历如下

void PreOrderTraverse(BiTree *root)
{
	if (root == NULL)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>return ;
<span style="white-space:pre">	</span>}
	
	printf("%2c",root->data);
	PreOrderTraverse(root->lChild);
	PreOrderTraverse(root->rChild);
	
}

咱们看一下遍历到底的时候是什么样子的:

遍历到底,还是很容易想的,因为严蔚敏书上有一句比较好的话就是:由于二叉树是一种非线性的结构,每个节点都可能有两颗子树,因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上,从而便于遍历。从这句话我们可以看出,“递归到底” 的操作实际上和链表的遍历时一样的(由此容易想到其实链表的遍历也可以用递归来实现)。递归的难点不在于递归到底,而在于怎么返回。那么咱们接着看,从最底下第一次返回是个什么样子。是不是又变成图2的样子(从最底层返回后PreOrderTraverse函数中的root 是下面的那个root)。

那么这时候函数执行到哪里了呢,应该紧接着执行如下语句:

PreOrderTraverse(root->rChild);

去遍历右边的结点。

为什么说这个东西呢,就是因为即使拿着图,也很难和递归调用的代码对应上,关于这个问题我思考了很久,但最后只有这种通过看指针的流动来感觉递归流程的方法,才让我感觉比较舒服。

想到这里,是不是感觉,什么前中后序遍历,原来是一样的嘛,只不过把打印放在前,中或者后而已。

如果基本上能想明白这个道理,那么建议你随便画个树的图,然后用指针流动这种方法,把树的前中后序遍历结果写出来,如果能非常容易写出来,那么说明树的学习入门了。

关于递归,我一直都在找比较好的理解的方法,也许你不会对递归感到迷惑那么我实在羡慕你,也许你会想起递归就怕,如果属于后一种情况,强烈的建议你试一试本文所讲述的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值