昨天做了个二叉树的笔试题 大概十几分钟就做完了 但是有些疑惑 想到了一个新题 不知道有没有人能解 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
woai110120130
V2EX    程序员

昨天做了个二叉树的笔试题 大概十几分钟就做完了 但是有些疑惑 想到了一个新题 不知道有没有人能解

  •  
  •   woai110120130 2015-06-15 22:13:39 +08:00 6727 次点击
    这是一个创建于 3775 天前的主题,其中的信息可能已经有所发展或是发生改变。
    下面是笔试题目: 根据用户的输入的字母, 构建一个满二叉树, 然后打印出它的所有从根到叶子的路径. (假设用户的输入字母的个数恰好满足构建一个满二叉树)
    例如: 输入 a b c d e f g后构建成下面的树
    a
    b c
    c d e f 就是这样的一个满二叉树

    然后输出 abc,abd,ace,acf
    要求: 使用任意一种你熟悉的语言即可。
    然后菜鸟我是这样做的

    /**
    * Created by tangtang on 15/6/15.
    */
    public class Tree {


    public static void main(String[] args)
    {
    byte[] byteArray={'D','B','A','C','F','E','G'};

    Node root=new Node();




    for (byte b: byteArray)
    {
    root.insert(b);

    }

    root.traverseBiTree("");
    }


    static class Node{

    private byte value=-1;

    private Node leftChild;
    private Node rightChild;

    public Node getLeftChild()
    {
    return leftChild;
    }

    public Node getRightChild()
    {
    return rightChild;
    }


    public byte getValue()
    {
    return value;
    }

    public void setLeftChild(Node leftChild)
    {
    this.leftChild=leftChild;
    }

    public void setRightChild(Node rightChild)
    {
    this.rightChild=rightChild;
    }

    public void setValue(byte value)
    {
    this.value=value;
    }


    /**
    * 遍历加打印 遍历路径
    * 思路 让上一层都产生一个string
    * @return
    */
    public void traverseBiTree(String str)
    {
    str+=(char)value;
    if (leftChild==null&&rightChild==null) {
    System.out.println(str);
    return;
    }

    if (leftChild!=null)
    {
    leftChild.traverseBiTree(str);
    }

    if (rightChild!=null)
    {
    rightChild.traverseBiTree(str);
    }
    }




    public void insert(byte c)
    {
    if (value==-1) {
    value = c;
    return;
    }

    if (c>value)
    {
    if (rightChild==null)
    rightChild=new Node();
    rightChild.insert(c);
    }else {
    if (leftChild==null)
    leftChild=new Node();
    leftChild.insert(c);
    }

    }

    }
    }

    用java写的 有点长 不过去掉set get方法也非常简洁了


    在做的过程产生了这样一个问题
    如果 试题问的 就是输入a b c d e f g 生成上边的那个满二叉树 要求下一层的孩子都大于上一层的孩子 并且 右孩子都大于左孩子 如何生成该 上面的那个满二叉树? 好好想想 这个问题还是很困难的
    要求只能一个一个的插入 不可以先排序再生成二叉树
    56 条回复    2015-06-16 22:16:33 +08:00
    Heartwork
        1
    Heartwork  
       2015-06-15 22:33:02 +08:00 via Android
    听说过平衡二叉树或者红黑树么?它们在插入过程当中通过自旋就能满足你说的要求。
    woai110120130
        2
    woai110120130  
    OP
       2015-06-15 22:37:36 +08:00
    @Heartwork 这位兄台 你实现一个看看 平衡二叉树 并不能满足需求 不信你做做看
    yangff
        3
    yangff  
       2015-06-15 22:44:16 +08:00 via Android
    treap
    woai110120130
        4
    woai110120130  
    OP
       2015-06-15 22:57:02 +08:00
    @yangff 1.如果v是u的左孩子,则key[v] < key[u].
    2.如果v是u的右孩子,则key[v] > key[u].
    3.如果v是u的孩子,则priority[u] > priority[u]. 这是树堆的特点 好像并不满足
    Heartwork
        5
    Heartwork  
       2015-06-15 23:01:36 +08:00
    @Heartwork 恩,果然不行。

    树的底层使用数组表示,插入算法使用普通的数组插入,可以保证最后的树为符合条件的完全树。

    不过整体算法复杂度为O(n^2)。

    你有更好复杂度的算法没?
    yangff
        6
    yangff  
       2015-06-15 23:03:49 +08:00 via Android
    @woai110120130 priority=key
    woai110120130
        7
    woai110120130  
    OP
       2015-06-15 23:04:27 +08:00
    @Heartwork 嗯 我并没有解出来 想的不错 但是一写发现还是有问题
    binux
        8
    binux  
       2015-06-15 23:05:00 +08:00
    #!/usr/bin/python
    #如果你会用数组表示二叉树,就会知道这个问题有多简单

    l = 'abcdefg'

    for i in range(len(l)/2+1, len(l)+1):
    __result = []
    __while i:
    ____result.append(l[i-1])
    ____i /= 2
    __result.reverse()
    __print ''.join(result)
    woai110120130
        9
    woai110120130  
    OP
       2015-06-15 23:09:18 +08:00
    @binux 你这个并不对 如果打乱abcdefg的顺序呢 比如插入 t c b d a f k呢
    binux
        10
    binux  
       2015-06-15 23:10:22 +08:00
    @woai110120130 你只要求是满二叉树啊,又没规定是怎么插入的
    woai110120130
        11
    woai110120130  
    OP
       2015-06-15 23:11:21 +08:00
    看看最后那段话 问题在最后
    binux
        12
    binux  
       2015-06-15 23:17:26 +08:00   1
    @woai110120130 谁去看最后啊,那不就是个小根堆啊
    Heartwork
        13
    Heartwork  
       2015-06-15 23:19:25 +08:00
    @binux

    英雄所见略同:)

    顺序插入的话,直接把元素放到末尾就好。

    不过考虑如果数据为随机插入的话,整个算法就退化为有序数组的插入算法了。

    整个过程还是O(log n^2)
    Heartwork
        14
    Heartwork  
       2015-06-15 23:22:40 +08:00
    @binux

    不是堆,堆的性质只能保证parent的value比child小。
    wodesuck
        15
    wodesuck  
       2015-06-15 23:23:43 +08:00 via Android
    @binux +1 不就是个小根堆嘛
    binux
        16
    binux  
       2015-06-15 23:24:42 +08:00
    @Heartwork 既然对于小根堆来说,左右孩子大小关系没有影响,你交换一下不就好了。。。
    wodesuck
        17
    wodesuck  
       2015-06-15 23:25:58 +08:00 via Android
    @Heartwork 二叉堆为什么有退化的问题。。不是严格O(nlogn)的吗。。
    zwzmzd
        18
    zwzmzd  
       2015-06-15 23:29:09 +08:00
    我觉得这个题目限制不严,比如说不能先排序,那如果之后的算法内比较再交换允许吗?一般的算法题,限制时间复杂度和空间复杂度,都是很明确的。

    提供个思路:用一维数组表示满二叉树,你可以顺序把元素全填进去,然后快排调整
    Heartwork
        19
    Heartwork  
       2015-06-15 23:29:17 +08:00 via Android
    @binux
    无法避免a的兄弟节点的孩子比a大的状况
    Heartwork
        20
    Heartwork  
       2015-06-15 23:30:12 +08:00 via Android
    @wodesuck
    不是,我的方案是使用有序数组。
    zwzmzd
        21
    zwzmzd  
       2015-06-15 23:33:43 +08:00
    再者,基于比较的排序算法已经证明了时间复杂度下界是O(nlogn)。你要的结果里面所有元素已经有序了,所以如果你的算法基于比较,也不可能超过这个下界。
    binux
        22
    binux  
       2015-06-15 23:36:12 +08:00
    @Heartwork 并不违反 「下一层的孩子都大于上一层的孩子 并且 右孩子都大于左孩子」吧,那有什么关系?
    zwzmzd
        23
    zwzmzd  
       2015-06-15 23:37:08 +08:00
    @binux
    @Heartwork
    只交换是不行的,但再加上向下调整就行了
    binux
        24
    binux  
       2015-06-15 23:40:46 +08:00
    @zwzmzd 为什么不行?首先小根堆保证了左右两个元素比父元素大,交换了依然满足这个条件啊。
    Heartwork
        25
    Heartwork  
       2015-06-15 23:45:26 +08:00 via Android
    @binux
    说反了,应该是 无法避免a的兄弟节点的孩子比a小的状况

    这样:

    a
    b z
    c d
    c和d是b的孩子,但是比z小。
    zwzmzd
        26
    zwzmzd  
       2015-06-15 23:46:28 +08:00
    @binux 就说一个简单的小根堆 1,5,2,6,7,3,4 (数组表示)
    交换中间层元素5,2之后,会变成 1,2,5,6,7,3,4,不符合小根堆性质了

    结论:已经调整完毕的小根堆,不可随意交换某两个孩子的位置。
    Heartwork
        27
    Heartwork  
       2015-06-15 23:48:50 +08:00 via Android
    有序的完全二叉树为一有序序列,在插入过程中插入位置之后的节点都需要向后移动,所以这个题目等同于求一个有序数组的插入算法…

    这样应该没问题了。
    woai110120130
        28
    woai110120130  
    OP
       2015-06-15 23:51:12 +08:00
    @zwzmzd 其实说不能先排序 是考虑到性能的问题 每次都要排序 会浪费极大的性能 其实这也不是真实中需要的算法了 只不过是在做题的过程中产生的思考罢了 看了楼上的 觉得还没有满意的答案 为什么没有人动手实现一下呢 想的永远回比做起来简单
    woai110120130
        29
    woai110120130  
    OP
       2015-06-15 23:54:52 +08:00
    @Heartwork bingo 其实这么做 把先排序换成了后排序 我在想有没有更好的办法
    Heartwork
        30
    Heartwork  
       2015-06-16 00:03:31 +08:00 via Android
    后排序会导致插入过程中树无法保持有序的条件,这个题目就退化为给一个数组排序了。

    底层如果使用二叉树的话,需要维护每一个元素的“前驱”和“后继”,前驱和后继指value的前一个和后一个。

    这样插入时需要遍历插入位置之后的元素,复杂度和数组的相同。
    binux
        31
    binux  
       2015-06-16 00:05:35 +08:00
    @Heartwork
    @zwzmzd 难道 「下一层的孩子都大于上一层的孩子」 这句话的意思是,大于上一层的所有节点?
    woai110120130
        32
    woai110120130  
    OP
       2015-06-16 00:07:31 +08:00
    @Heartwork 洗澡的时候想了想 这并没有什么卵用 排完序之后 还需要重新组织二叉树 还是违背题的
    Heartwork
        33
    Heartwork  
       2015-06-16 00:08:58 +08:00 via Android
    @binux

    我是这样理解的。就是一颗有序的完全二叉树。
    woai110120130
        34
    woai110120130  
    OP
       2015-06-16 00:09:35 +08:00
    @Heartwork 亲 你觉得可行 可以写出来让大家学习学习
    Heartwork
        35
    Heartwork  
       2015-06-16 00:09:54 +08:00 via Android
    @woai110120130
    所以底层用数组就行了
    binux
        36
    binux  
       2015-06-16 00:39:02 +08:00
    @Heartwork

    1、如果这么理解,这个题目是有问题的。
    既然第 n 层 全部大于 第 n-1 层,「右孩子都大于左孩子」没有任何意义,在同一层之间交换元素,没有任何影响和副作用。那么这个 「右孩子都大于左孩子」的要求,随便交换一下元素就可以了。

    2、其次,这个问题不是完全排序的。
    只要使用快速划分,让数组左边一半小于右边一半,然后对左边继续前面的过程。再加上1所诉的的交换元素,就可以建立起符合要求的树了。复杂度只有 O(n)
    woai110120130
        37
    woai110120130  
    OP
       2015-06-16 08:41:56 +08:00
    楼上的诸位说这么多只不过是纸上谈兵罢了 没有一个肯做出来用事实说话
    Heartwork
        38
    Heartwork  
       2015-06-16 08:45:08 +08:00 via Android
    @binux
    n层大于n-1层(a)与右孩子大于左孩子(b)是两个独立的条件,写两个例子。
    a成立b不成立:
    a
    c b
    b成立a不成立:
    b
    a c
    这两个条件共同约束下只能是有序序列。
    Heartwork
        39
    Heartwork  
       2015-06-16 08:48:00 +08:00 via Android
    @binux
    对了,你说的2的“调整”如果满足a和b,就是一个快速排序。
    binux
        40
    binux  
       2015-06-16 09:03:12 +08:00
    @woai110120130 事实?你自己问题都说不清楚,让人家找事实?你写得出测试用例吗?

    @Heartwork 不是的
    a
    b c
    d e f g
    成立

    a
    b c
    e g d f
    也成立

    同层之间,只要 2n 和 2n+1 做个交换就能满足条件 b
    Heartwork
        41
    Heartwork  
       2015-06-16 09:14:03 +08:00 via Android
    @binux 恩,不错,是这样
    binux
        42
    binux  
       2015-06-16 09:17:43 +08:00
    @Heartwork 所以只要层间排序,不需要层内排序。
    如果一次解决就是做一半快速划分,如果插入就是层内做大根堆。
    都是 O(n) 的
    Heartwork
        43
    Heartwork  
       2015-06-16 09:38:18 +08:00 via Android
    @binux 堆的那个恐怕不行吧。怎么处理插入一个比该层元素都小的值?
    binux
        44
    binux  
       2015-06-16 10:07:12 +08:00
    @Heartwork 大根堆顶往下推,不然为什么是大根堆呢
    zwzmzd
        45
    zwzmzd  
       2015-06-16 10:17:43 +08:00 via Android
    这个题目根据描述很可能有歧义,我觉得楼主你在叫人写代码之前应该给出几个测试用例,这样别人才好着手去写
    woai110120130
        46
    woai110120130  
    OP
       2015-06-16 12:03:59 +08:00
    @zwzmzd 哈哈 没有其他的意思 就是讨论学习 我并没有测试用例 确实有人理解的不太正确 嗯等晚上下班在写吧
    woai110120130
        47
    woai110120130  
    OP
       2015-06-16 12:05:12 +08:00
    @binux 不是我 我出题的意思是
    隐藏 感谢回复者 Reply 40
    binux 3 小时 0 分钟前
    @woai110120130 事实?你自己问题都说不清楚,让人家找事实?你写得出测试用例吗?

    @Heartwork 不是的
    a
    b c
    d e f g
    成立

    a
    b c
    e g d f 这个不成立 右边的都要大于左边的 可能是我描述的不清楚 实在不好意思哈
    binux
        48
    binux  
       2015-06-16 12:33:48 +08:00
    @woai110120130 如果右边的都要大于左边,这棵树可以被证明是完全排序的,这个题目就更没意义了。
    billwsy
        49
    billwsy  
       2015-06-16 14:43:42 +08:00
    你要求的所谓树用数组来表示那就是有序数组嘛,要求可以快速随机访问插入那就是块状链表了,效率是sqrt(n)
    woai110120130
        50
    woai110120130  
    OP
       2015-06-16 14:46:57 +08:00
    @billwsy 其实更像是杨辉三角 但是要求动态插入 而不是排好序再排列
    billwsy
        51
    billwsy  
       2015-06-16 14:52:26 +08:00
    Oops, 似乎弄错了,我把条件加强了。。
    billwsy
        52
    billwsy  
       2015-06-16 15:02:59 +08:00
    这样做如何:每一层对应一个大根堆,插入时二分查找到需要插入的层,O(lg lg n),将其根剔除并插入元素,将其根插入下一层,每次执行O(lg n),最多执行lg n次,效率O(lg^2 n);总效率O(lg lg n + lg n * lg n) = O(lg^2 n)。如果用数组来存储堆,并且将所有数组头尾相接起来,可以以大数组来解释二叉树。
    这样的话,总效率O(n lg^2 n),缺点是插入时会破坏所有原有的父子关系。
    billwsy
        53
    billwsy  
       2015-06-16 15:05:52 +08:00
    Oops,又漏了。用大数组来解释时,会出现左儿子大于右儿子的情况,访问时交换它们修正就可以了
    billwsy
        54
    billwsy  
       2015-06-16 15:07:48 +08:00
    发现@binux 已经给出正解了…默默匿了…
    billwsy
        55
    billwsy  
       2015-06-16 15:10:45 +08:00
    好吧,按@woai110120130 47楼的解释,那我的解答就是49楼提到的块状链表了……
    洗洗睡了……
    woai110120130
        56
    woai110120130  
    OP
       2015-06-16 22:16:33 +08:00
    @billwsy 好的吧
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2608 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 15:33 PVG 23:33 LAX 08:33 JFK 11:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86