Binary tree problems

bt

Paper Link : http://cslibrary.stanford.edu/110/

前言

阅读了Stanford CS Education Library中的Binary Trees,发现你们的题目很好,很适合初学者练习二叉树.也很适合对递归不熟悉的同学.这篇blog记录下来一些很棒的题目.



递归法则:

设计法则:

1.基准情况:必须总有某些基准情形,它无序递归就能解出
2:不断推进:对于需要递归求解的情形,每一次递归调用都必须要使求解状况朝接近基准情形方向推进
3设计法则:假设所有的递归调用都能运行
4:合成效益法则:求解一个问题的同一个实例,切勿在不同的递归调用中做重复性的工作
这是《数据结构与算法C语言描述》里,对于设计递归的思路。非常nice。

不要在自己脑子里模拟递归,这样只能自讨苦吃.
把递归当成这一层问题的做法,其他的不用考虑.
比如下面汉诺塔:
n层的时候,首先把n-1层从A移动到B,再把在A最后一层移动到C,最后把n-1层从B移动到C。这样,就完成了n层的任务。

def move(n,a,b,c):
    if n==1:
        print(a,"-->",c)
    else:
        move(n-1,a,c,b)
        move(1,a,b,c)
        move(n-1,b,a,c)

在脑子里,时刻注意递归的设计法则。并不要模拟递归,而是想清楚这一次函数调用做什么事情。就OK了。

补充

在作者的The Great Tree problem文章里,作者论述的一句话:

Trust that the recursive calls return correct output when fed correct input – make the leap of faith. Look at the partial results that the recursive calls give you, and construct the full result from them. If you try to step into the recursive calls to think how they are working, you’ll go crazy.
translation:请相信你的递归函数。请注意递归调用时产生的局部的结果,并从中构造完整的结果.如果你试图进入递归调用,并思考它是怎么工作的,你一定是疯了 (翻译的不好>﹏<)

和我通过查询大量资料,得出的结论一样(。・∀・)ノ



Problem

size()

求二叉树节点个数?
这题虽然不难,但是拿来学递归就很棒了.

int size(struct* node){
    if(node ==NULL)       //base case 基准情况
        return 0;
    int lsize = size(node->lchild);     //求左子树的节点个数
    int rsize = size(node->rchild);     //求右子树的节点个数
    return lsize+rszie+1;               //合并,别忘了左子树和右子树的父亲节点,要加1
}

maxDepth()

求深度.
同样,不难,但是练习递归很棒

int maxDepth(struct node* node){
    if(node == NULL)           //base case 基准情况
        return 0;
    int ldepth  = maxDepth(node->lchild);     //求左子树的深度
    int rdepth = maxDepth(node->rchild);      //求右子树的深度
    return ldepth>rdepth?++ldepth:++rdepth;   //比较那个值大,并加上父亲节点,1
}

minValue()

求最小值.
这题大多数人都是用迭代,为了练习递归,用递归( ̄▽ ̄)”
iterator solution

int minValue(struct node* node){
    while(node->lchild !=NULL)
        node = node->lchild;
    return node->data;  
}

recursion solution

int minValue(struct node* node){
    if(node->lchild == NULL)
        return node->data;
    else
        return minValue(node->lchild);
}

hasPathSum()

是否有从root到树叶的路程,其上节点和为sum
这题没有上面几题,很容易想到怎么递归。一个trick方法,把节点和改为了每经过一个节点,就从sum上减去该节点的值。

int hasPathSum(struct node* node,int sum){
    if(node == NULL)        //base case。基准情况。如果遍历一个路径,sum被减为0,则代表这条路径的和为sum
            return sum==0;
    int subsum = sum-node.data;
    return hasPathSum(node->lchild,subsum) 
        || hasPashSum(node->rchild,subsum);
}

printPaths()

打印所有路径
这题挺难的,paper上也给了hint,用一个helper函数:printPathsRecur(struct node* node,int path,int pathLen)。也就是前序遍历,把每一个节点保存在path的数组里,pathLen控制填入的位置.具体看代码吧.

void printPaths(struct node* node){
    int maxdepth =  maxDepth(node);
    int* path = new int[maxdepth];
    printPathsRecur(node,path,0);
}
void printPathsRecur(struct node* node, int path[], int pathLen) {
    if (node == NULL)
        return;
    path[pathLen++] = node->data;
    if (node->lchild == NULL && node->rchild == NULL) {
        for (int i = 0; i<pathLen; i++)
            cout << path[i] << " ";
        cout << "\n";
        return;
    }
    printPathsRecur(node->lchild,path,pathLen);
    printPathsRecur(node->rchild, path, pathLen);
}

mirror()

镜像二叉树.
挺简单的,练习练习递归.

void mirror(struct node* node) {
    if (node == NULL)
        return;
    struct node* lchild = node->lchild;
    node->lchild = node->rchild;
    node->rchild = lchild;
    mirror(node->lchild);
    mirror(node->rchild);
}

doubleTree()

把每一个节点复制一遍,并添加到其左子树.
还是练习递归的题目

void doubleTree(struct node* node){
    if(node == NULL)
        return;
    doubleTree(node->lchild);
    doubleTree(node->rchild);
    struct node* newnode = new node(node->data);
    struct node* temp =node->lchild;
    node->lchild = newnode;
    newnode->lchild = temp;
}

sameTree()

二棵二叉树是一样的吗(指的是值)
我觉得这题给我的感觉是这个:

return (a->data == b->data) &&
            sameTree(a->lchild,b->lchild) &&
            sameTree(a->rchild,b->rchild);

把递归结果 &&,这样结合了左子树和右子树的结果cool

int sameTree(struct node* a,struct node* b){
    if(a == NULL && b == NULL)
        return true;
    else if(a !=NULL && b !=NULL){
        return (a->data == b->data) &&
            sameTree(a->lchild,b->lchild) &&
            sameTree(a->rchild,b->rchild);
    }
    else
        return false;
}

countTrees()

给你N个节点,值为 [ 1... N ] ,问有多少种树.
百度了下,这是到ACM题count Tree,看了看一些blog和百科,发现这是一个很有名的数列,叫卡特兰数(In Wiki:Catalan)。知乎上的大佬也解释了为什么这个题会变成卡特兰数 Link。看来以后要学一些数学了。
上面都是题外话,这题没有ACM那么难,就是帮你厘清递归

int countTrees(int numKeys){
    if(numkeys <= 1)              //base case,当numkey<=1时,只有一种树
        return 1;
    int sum = 0;
    int left,right,root;
    for(root=1;root<=numkeys;root++){
        left = countTrees(root-1);          //left:左子树root-1个节点,有多少种树
        right = countTrees(numKeys-root);   //right:右子树numKey-root个节点,有多少种树
        sum+=left*right;           //将left*right,计算种类
    }
    return sum;
}

isBST()

binary search tree
paper指定用minValue和maxValue,paper上面说效率会很低.但是我觉得这个方法只是为了帮助我们厘清binary search tree。code就是按对应定义写就行了.

int isBST(struct node* node){
    if(node == NULL)
        return 1;
    if(node->lchild !=NULL && minValue(node->lchild) > node->data)
        return false;
    if(node->rchild !=NULL && maxValue(node->rchild) < node->data)
        return false;
    if(!isBST(node->lchild) || !isBST(node->rchild))
        return false;   
    return true;
}

更好的方法,写一个helper函数int isBSTrecur(struct node* node,int min,int max){。详细见code

int isBST(struct node* node){
    return isBSTrecur(node,INT_MIN,INT_MAX);
}
int isBSTrecur(struct node* node,int min,int max){
    if(node == NULL)
        return true;
    if(node.data <min || node.data >max)
        return false;
    return isBSTrecur(node->lchild,min,node->data) &&
           isBSTrecur(node->rchild,node->data,max); 
}

OOP style vs Recursive style

在JAVA solution里,paper里写:root pointer是类内部private数据,所以按照OOP,这些数据是不能暴露给使用类的人的,所以写public函数,很难写成递归版本.
这里,作者用了一个方法来解决这个问题: 一个简单的函数,用于给使用类的人,一个private的递归函数:用于解决问题.比如:

public:
int Class::lookup(int data){
    return loopup(root,data);
}
private:
int Class::lookup(Node node,int data){
    //recursive code
}

这样,就解决了这个问题
(书上都是用C代码写,所以没有出现这个问题。所以用C++写二叉树class,会遇到了这个问题,以前我也作者的做法一样 ( ^-^)ρ(╯^╰))

猜你喜欢

转载自blog.csdn.net/weixin_41256413/article/details/81412960