您的位置:首页 > 其它

算法竞赛入门经典第6章例题(2):二叉树部分+四分树

2016-07-21 17:35 441 查看

6−6这是一道有关二叉树的1题目,但是如果你真的对它用二叉树进行模拟就入坑了,不需要真正的实现二叉树,也无关小球的编号大小,我们只需要判断第i次小球在第d层的行为既可。假设向左为0向右为1,这个行为序列的规律是:000,100,010,110,001,101,011,111。即第k位每2k−1变动一次,根据这个规律,我们可以直接求解最终的位置。

def get_track(i,D):
ans,i,pos = '',i-1,1
for d in range(0,D-1):
k = i>>d
if k%2:pos = pos*2+1
else:pos*=2
print('*****:',pos)
def proc():
num = int(input())
while num>0:
line = input()
line = line.split(' ')
D,I = int(line[0]),int(line[1])
get_track(I,D)
num-=1
proc()


上面的思路相对麻烦一点,书上提供了一种更加简单的方法,这种方法有点递归的思想:

对于第i个小球:,

if i%2==1那么它就是第(i+1)/2个向左的小球,否则就是第i/2个向右的小球。那么该层的行为就可以确定。同时,对于左/右子树,i = next(i),继续这个判断既可。相对于我自己的办法,这种思路逻辑更加清晰,非常巧妙的解决了问题!代码如下:

def get_track(i,D):
ans,pos = '',1
for d in range(0,D-1):
if i%2:pos,i = pos*2,(i+1)>>1
else:pos,i=2*pos+1,i>>1


例题6−7这题标准解法本身不复杂,就是不停的读入然后创建一个二叉树,再bfs层序遍历,同时进行检测。

但是我这里要提供一个我自己的思路,利用输入的路径进行排序可以直接得到层序遍历的结果。比较的函数返回一个tuple:(len(path),path),len(path)越小说明depth越小,在depth相同的情况下应该看谁在左边,因为字符串中‘L’<‘R’,所以可以直接比较字符串…..最后同样是根据排序好的节点建立一棵树,但是可以发现错误的输入就提前结束。

def proc_input():
line = input()
while line!='':
nodes = []
line = line.split(' ')
for node_expr in line:
if node_expr=='()':continue
node_expr = node_expr[1:len(node_expr)-1]
node_expr = node_expr.split(',')
node = (node_expr[0],node_expr[1])
nodes.append(node)
nodes.sort(key = lambda x: (len(x[1]),x[1]))
if test(nodes):print([i[0] for i in nodes])
else:print(-1)
line = input()

def test(nodes):
root = nodes[0] if nodes[0][1]=='' else None
if root==None:print('no root');return False
tree = [root,None,None]
curr,parent = tree,root
for node in nodes[1:]:
curr = tree
for way in node[1]:
if curr==None:break
parent = curr
if way=='L':curr = curr[1]
else:curr = curr[2]
if curr!=None:return False#repeat
if parent[0][1]==node[1][:-1]:
if way=='L':parent[1] = [node,None,None]
else:       parent[2] = [node,None,None]
else:print('len:',parent[0],node);return False
return True
proc_input()


补充,还可以使用数组来表达二叉树,代码如下:

注意,下面的代码只针对上面的题目。

cnt,root = 1,1
left,right,halv_val = [0,0],[0,0],[0,0]
def newtree():
left[root],right[root],halv_val[root] = 0,0,False
def newnode():
global cnt,u
u,cnt = cnt+1,cnt+1
left.append(0);right.append(0);halv_val.append(False)
return u
def addnode(node):
global cnt,root
u,way = 1,node[1]
for d in way:
if d=='':halv_val[root] = node
elif d=='L':
if left[u]==0:left[u]=newnode()
u = left[u]
else:
if right[u]==0:right[u]=newnode()
u = right[u]
if halv_val[u]==True:assert(1<0)
halv_val.append(node)
newtree()
print(halv_val,left,right)


6−8采用了数组来实现二叉树。其实在python里面直接构造树反而更加方便一点。另外一个就是从两中遍历序列构造出二叉树并且进行dfs。

def createtree(inorder,postorder):
global left,right,halv_val,cnt,root
left,right,halv_val,cnt,root = [0],[0],[0],0,0
def maketree(inorder):
root = postorder.pop()
rootindex = newnode()
rootpos = inorder.index(root)
lt,rt = inorder[0:rootpos],inorder[rootpos+1:]
halv_val[rootindex] = root
if rt!=[]:u=maketree(rt);right[rootindex] = u
else:right[rootindex] = 0
if lt!=[]: u = maketree(lt);left[rootindex] = u
else:left[rootindex] = 0
return rootindex
return maketree(inorder)

def search():
m = (1<<100,1<<100);L=[]
def dfs(rootindex=1):
nonlocal m
root = int(halv_val[rootindex])
if left[rootindex]==0 and right[rootindex]==0:
m = min(m,(sum(L)+root,root))
if left[rootindex]!=0:L.append(root);dfs(left[rootindex]);L.pop()
if right[rootindex]!=0:L.append(root);dfs(right[rootindex]);L.pop()
dfs()
print(m)

createtree(list('3214576'),list('3125674'))
search()
createtree([7,8,11,3,5,16,12,18],[8,3,11,7,16,18,12,5])
search()


6−9平衡的天平,在SICP里面也有类似的题目,不过那个构造相对复杂,这个就很精简了。递归的判断左右子天平是不是平横且自己是不是平衡。处理输入的部分就懒得写了…

def create_mobile(preorder):
wl,dl,wr,dr = preorder.pop()
b1,b2 = True,True
if wl==0:b1,wl = create_mobile(preorder)
if wr==0:b2,wr = create_mobile(preorder)
return (b1 and b2 and wl*dl==wr*dr),wl+wr
print(create_mobile([[0,2,0,4],[0,3,0,1],[1,1,1,1],[2,4,4,2],[1,6,3,2]][::-1]))


6−10下落的叶子,和6-9非常的相似,我这里也略去对输入的处理了。

def compute(preorder):
value = {}
def fall_tree(preorder,horizon):
root = preorder.pop()
value[horizon] = root+value[horizon] if horizon in value else root
if preorder[-1]!=-1:fall_tree(preorder,horizon-1)
else:preorder.pop()
if preorder[-1]!=-1:fall_tree(preorder,horizon+1)
else:preorder.pop()
return
fall_tree(preorder[::-1],0)
print([value[k] for k in sorted(value.keys())])
compute([5,7,-1,6,-1,-1,3,-1,-1])
compute([8,2,9,-1,-1,6,5,-1,-1,12,-1,-1,3,7,-1,-1,-1])


6−11这道题书上的办法最简洁,两次遍历既可,但是我的办法更高效。

(a)首先同时遍历两颗树,如果有一个同层次的节点是黑色的,就不需要遍历另外一棵树的节点了。

(b)注意,对于(a),如果另一个树的对应节点是中间节点,应该将该节点下的四个字节点删除。

(c)其次如果两个都是中间节点递归的计算既可。如果两个都是白色也不需要计算了。

(d)比较麻烦的是一个是中间节点,一个是白色节点。这时候使用另一个函数去单独遍历该中间节点,注意遍历前应该将该中间节点重新压会序列里面。这样方便写代码。

def proc(p1,p2):
sum = 0
def remov(p):
for i in range(4):p.pop()
def compute(order,num):
nonlocal sum
root = order.pop()
if root=='f':sum+=num
elif root == 'p':
for i in range(4):compute(order,num>>2)
def quadtrees(preoder1,preoder2,num):
nonlocal sum
root1,root2 = preoder1.pop(),preoder2.pop()
if root1=='f' or root2=='f':
sum+=num
if root1=='p':remov(preoder1)
if root2=='p':remov(preoder2)
elif root1 == 'e' and root2=='e':return
elif root1=='p' and root2=='p':
for i in range(4):quadtrees(preoder1,preoder2,num>>2);
elif root1=='p':preoder1.append('p');compute(preoder1,num)
else:           preoder2.append('p');compute(preoder2,num)
quadtrees(p1[::-1],p2[::-1],1024)
print(sum)
proc(list('ppeeefpffeefe'),list('pefepeefe'))
proc(list('peeef'),list('peefe'))
proc(list('peeef'),list('peepefefe'))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: