最近写了一些递归的题目,但是总觉得不够直观。
//一棵树的中序遍历
void dfs(TreeNode* root){
if(!root) return ;
if(root->left) dfs(root->left);
visit(root);
if(root->right) dfs(root->right);
}
再比如:
// 543. 二叉树的直径
class Solution {
public:
int depth(TreeNode* root){
if(root==NULL) return 0;
return max(depth(root->left),depth(root->right))+1;
}
int diameterOfBinaryTree(TreeNode* root) {
if(root==NULL) return 0;
// 分为三种情形,一,在左子树取得最大。二、在右子树取得最大。三、跨过根节点取得最大
return max(max(diameterOfBinaryTree(root->left),diameterOfBinaryTree(root->right)),
depth(root->left)+depth(root->right));
//注意这里的分支数比depth小一,加二减二抵消了
}
};
我从来是一个很笨的人,什么事情不动手,亲自做一做,便总是想不通,符号语言在我眼里必须变成直观的才行。
所以,递归解释为自己调用自己让我摸不着头脑。总觉得还是不够直观。
我小时候有一个光荣的工作:厨房购物小能手。比如常常要买柴米油盐了。
今天妈妈再一次在炒菜的时候没有盐了,于是妈妈面前有了一个问题如何得到盐?
于是妈妈把钱教给我,让我解决问题。
我想这个问题不就是上网订购么?三下五除二京东下了单。
之后商店的盐有了,只是还没有到我手里,后来,我等了半天,快递小哥送了一箱盐到我家。
我呢,马上把盐交给母亲大人。
这之中是怎么解决问题的呢?
// 妈妈买盐->我买盐->商店提供盐->快递给我->我交给妈妈
bool buy_salt(people){
if(store_has_salt) return true;
return buy_salt(next_people);
}
这时候似乎说明了一些问题:
这里面的人仅仅做了运输的作用,像一条U型线。或者叫回旋镖。只不过着这里在天上飞着的是一箱盐,前半段是虚线,表示请求还不知道具体有没有,到顶点,明白可行于是变成实线。
所以,这是一种玄学的回归,虚实相映,又像太极里面的金刚倒锥,前面似乎在蓄力,到顶点一锤定音。
到这里似乎这是一种聪明的思维方法:我不会,可以问问下一个人会不会?总有一天会遇到一个懂的人,把我们这群笨人,依次传递教会到我这里。
明白了做领导的感觉可以试验做一道题:
https://leetcode-cn.com/problems/friend-circles/description/
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
有几个朋友圈,专业一点叫有几个联通子图。乍一听,依然如此抽象,上来就是模式和概念,失去了直观性的体验,同时又变得难以理解。同样的,我们把问题摆到眼前,怎么来解决?
当然,你可能下意识觉得我不会,这样很好!你可以做一回领导,把球丢出去,让会做的人做。听起来挺好的,实际上呢?也的确如此!
为了避免失误,你大可以在你认识的人里可以广播:
不断联系认识的,朋友圈扩张到不能再扩大时,恭喜你找到一个。
然后,告知领导即可。有领导判断是不是重复了之类的小问题,看到问题扩散之后变得可以解决了。
void ask_someone_i_know(me){
while(each_one_mylist){//把我认识的人完全骚扰一遍
if(find_one_friend){
this_friend had visited;
ask_someone_i_know(my_friend);
}
}
return;
}
int findCircleNum(){
int ans=0;//计数
for(everyone){
if(he not be visited){
ask_someone_i_know(he);//把他认识的人都询问一遍
ans++;
}
}
return ans;
}
于是对话如下:
领导:小王你认识的人都给问问,我看看有几个圈子
小王:哦
领导:小李你认识的人也给问问,你是不是也有个圈子
小李:我和小王是一个圈子的
领导:走走走,下一个……
这一次,也是扔回旋镖,只不过是多个人分别扔而已,看最后落到手里的有几个。
注意到这里很像一种小时候玩的游戏,在河里丢石子,之后产生的波纹效果。
我像自己认识的人(A或B)中丢出一个飞镖,被自己认识的人继续像他认识的人丢出去,形成扇形的扩散效应。由于计算时候是一个一个来的看出来就好像是这样:
这就是dfs的一种直观的运行路线,像是递归的扩展(在维度上),不再只向一个人求助,每个人都可以有些求助的对象,于是依次求助之后,就像五指山一样,有高有低,但是必须从山脚走上山,然后回到山脚再上山,即是所谓的深度优先。