问题描述
在一个神奇的二叉树中,结构非常独特:
每层的节点值赋值方向是交替的,第一层从左到右,第二层从右到左,以此类推,且该二叉树有无穷多层。
小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;
问题分析
拿到这个问题,最容易想到的思路是:
- 根据
x,y大小构建出对应的二叉树; - 检索
x,y的位置; - 采用中序方式从
x,y中任一节点走到另一节点,对走过路径进行计数,结果便是路径长度。
但是,对以以上思路,若用传统方式通过构建类对象来构建二叉树,势必有较大的空间复杂度,且难以体现该二叉树的特点;对于x,y的位置,该用什么指标进行表示;从x走到y或者反过来,如何确定走左还是走右,又或者上还是下。面临以上问题,我决定选择新的思路。
通过对该二叉树进行分析,可以发现:
若从每一层(假设每一层都是满的)看,奇数层的数据是顺序且从到,偶数层的数据是逆序且从到,n为层数。
因此,我考虑使用二维数组代替传统的类来构建该二叉树,以数据所在层级为坐标i,从所在层从左往右位置为坐标j,来实现对数据的定位。而在构建二叉树时,先求出x,y所需要的层数max_n,再一层一层构建,每层长度为,若为奇数层便顺序添加数据,若是偶数层便逆序添加数据。
构建好二叉树后或者在构建二叉树时,我们可以得到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),也不需要完整构建该二叉树再进行遍历寻找,可以直接通过循环计算所得到。具体操作为:
- 通过对数算法找出
x,y各自所在层数xi,yi; - 从xi,yi中较大层起递减,计算该层是顺序还是逆序;
- 计算
x,y距离该层起始值的位置便为j,奇数层是与之差,偶数层是与之差。
最终,在未构建二叉树的情况下,便可得到该二叉树内任意两个节点之间路径的距离。
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)