206 小R的二叉树探险 | 豆包Marscode Al刷题

67 阅读5分钟

问题描述

在一个神奇的二叉树中,结构非常独特:

每层的节点值赋值方向是交替的,第一层从左到右,第二层从右到左,以此类推,且该二叉树有无穷多层。
小R对这个二叉树充满了好奇,她想知道,在二叉树中两个节点之间x, y的路径长度是多少。

例子

graph TD
  1((1));
  3((3));2((2));
  4((4));5((5));6((6));7((7));
  15((15));14((14));13((13));12((12));11((11));10((10));9((9));8((8));
  1---3;1---2;
  3---4;3---5; 2---6;2---7;
  4---15;4---14;5---13;5---12;6---11;6---10;7---9;7---8;

问题分析

拿到这个问题,最容易想到的思路是:

  1. 根据x,y大小构建出对应的二叉树;
  2. 检索x,y的位置;
  3. 采用中序方式从x,y中任一节点走到另一节点,对走过路径进行计数,结果便是路径长度。

但是,对以以上思路,若用传统方式通过构建类对象来构建二叉树,势必有较大的空间复杂度,且难以体现该二叉树的特点;对于x,y的位置,该用什么指标进行表示;从x走到y或者反过来,如何确定走左还是走右,又或者上还是下。面临以上问题,我决定选择新的思路。

通过对该二叉树进行分析,可以发现:

若从每一层(假设每一层都是满的)看,奇数层的数据是顺序且从2(n1)2^(n-1)2n12^n-1,偶数层的数据是逆序且从2n12^n-12(n1)2^(n-1),n为层数。

因此,我考虑使用二维数组代替传统的类来构建该二叉树,以数据所在层级为坐标i,从所在层从左往右位置为坐标j,来实现对数据的定位。而在构建二叉树时,先求出x,y所需要的层数max_n,再一层一层构建,每层长度为2(n1)2^(n-1),若为奇数层便顺序添加数据,若是偶数层便逆序添加数据。

构建好二叉树后或者在构建二叉树时,我们可以得到x,y所在位置xi,xj,yi,yj。接下来考虑路径方式:若用传统思路的从x出发到达y,对于每一步的方向都是不确定的,这会造成很大困难。而对于二叉树的性质,我想到了二叉检索树和Huffman编码的相关知识:

对于二叉树的任一节点,从根节点出发,向左标记为0,向右标记为1,便可对树的任一节点所在位置得到一串唯一的编码。

虽然这与Huffman编码并不一致,但是我们可以应用1类似的方法:

不从x或y出发,而是从根节点出发检索x,y的位置,将其向左或向右标记为0和1,对比x,y所得位置编码,相同的前缀表示从根节点出发到两点共同的路线,而编码不同处开始便表示x,y之间的转折点,该转折点到x,y步数之和也就是编码不同字符长度之和便是x,y之间路径长度。

对于x,y的位置,我们得到的数据是层级和在层级内的坐标,如何得到根节点到该位置的编码?再次对二叉树进行分析,可以得到以下结论:

若一个父节点的层级内坐标为j,则它的左孩子坐标为2j,右孩子坐标为2j+1。

因此,不需要从根节点出发寻找x,y位置,而是从x,y返回根节点,随着层级n不断递减,将xj,yj不断整除2返回父节点,若判断为父节点的左孩子位置编码前面加0,若判断为父节点的右孩子位置编码前面加1,最终返回根节点,层级n递减到0,便可得到x,y的位置编码。

最后,我发现,只需要根据二叉树构建的规律找到x,y的位置(i,j),便能根据位置编码得到两者间的路径长度,该过程与二叉树的构建规律无关。而根据二叉树构建的规律找到x,y的位置(i,j),也不需要完整构建该二叉树再进行遍历寻找,可以直接通过循环计算所得到。具体操作为:

  1. 通过对数算法找出x,y各自所在层数xi,yi;
  2. 从xi,yi中较大层起递减,计算该层是顺序还是逆序;
  3. 计算x,y距离该层起始值的位置便为j,奇数层是与2(n1)2^(n-1)之差,偶数层是与2n12^n-1之差。

最终,在未构建二叉树的情况下,便可得到该二叉树内任意两个节点之间路径的距离。

def solution(x: int, y: int) -> int:
    # write code here
    xs=""
    ys=""
    n=1
    xi,yi=-1,-1
    while(2**(n-1)<max(x,y)):
        if 2**n>x and xi==-1:
            xi=n-1
        if 2**n>y and yi==-1:
            yi=n-1
        n+=1
    xj=x-2**xi
    yj=y-2**yi
    if xi%2!=0:
        xj=2**xi-xj-1
    if yi%2!=0:
        yj=2**yi-yj-1
    print(x,xi,xj)
    print(y,yi,yj)
    ni=max(xi,yi)
    while(ni>0):
        if ni<=xi:
            xs=str(xj%2)+xs
            xj=xj//2
        if ni<=yi:
            ys=str(yj%2)+ys
            yj=yj//2
        ni-=1
    i=0
    while(i<min(len(xs),len(ys))):
        if xs[i]!=ys[i]:
            break
        else:
            i+=1
    xs=xs[i:]
    ys=ys[i:]      
    return len(xs)+len(ys)

if __name__ == '__main__':
    print(solution(11, 4) == 5)
    print(solution(2, 5) == 3)
    print(solution(7, 7) == 0)