牛客网3月模拟笔试-编程题

1、加减二叉树

二叉树是除了叶子节点之外所有的节点都最多有两个子节点的树。满二叉树则是除叶子节点外所有节点都有两个子节点的树,且所有叶子节点到根节点的距离都相等。

现在有一颗无限大的满二叉树,根节点编号为1。编号为i的节点的左儿子编号为2*i,右儿子编号为2*i+1(比如根节点1的左儿子为2,右儿子为3,2的左儿子为4,右儿子为5)。牛牛在这棵树上做一个游戏,他从妞妞那里得到了两个数n和k,妞妞希望他拿着数字0从根节点开始往下走,每次选择一条边移动,到达一个节点时选择加上这个节点的编号或者减去这个节点的编号。在走到第k个节点时,所得到的数字刚好为n。

这样的路径当然有很多,为了增加难度,妞妞决定让牛牛告诉她经过的节点的编号和最小的路径。

妞妞很聪明给出的问题都是一定存在答案的。

输入描述:

一行,两个数字n,k

输出描述

共khang,第i行输出第i步到达的节点编号,如果加上这个编号输出"  +",减去这个编号输出"  -"。

示例1

输入

3  2

输出

1 +

2+

示例2

输入

6  3

输出

1 -

2+

5+

备注

1<=n<=10000000000

1<=n<=2^k<=2^60

思路

此思路参考了大神的解题思路,理解了很久,把自己的想法写出来

因为1<=n<=2^k,所以最后第k层需要用的节点就是第一个和第二个。因为路径不一定是唯一的,但是通过第k层的第一个或者第二个点的路径是一定存在的(看完整个解析,你可能能领悟一点,我也是想了很久才领悟这个)。

如果走到的层数为k,此时如果一直走的是左侧,那么最大值为2^k-1,如果前k-1层走的是左侧,第k层走的是右侧,那么最大值为2^k。首先你需要确认,这里说的对不对。

如果这个时候,让你去找一个n=2^k-d的数。若一直走左侧,除了根节点是1其余都是偶数,无论做加法还是减法,最后结果一定是奇数。若先走左侧最后走右侧,则结果为偶数。因此按照d是偶数还是奇数选择最后的一步的走法,d为偶,则先左后右,d为奇,则一直左。

叙述到这个时候,想必你可能摸到什么眉目的吧。通过上述的描述,其实就是为了说清楚一个事,所有的1<=n<=2^k的数都可以使用k-1层的最左侧数以及第k层的第一个节点或者第二个节点来组成。

那怎么确定前面k-1个数的符号呢。因为n=2^k-d,所以前k-1层需要得到2^(k-1)-[d],[]为取小于等于d的最大偶数。所以前k-1层应该要构造 2^(k-1)-([d]/2) - ([d]/2),即你需要找到构造成([d]/2)的数,给其“-”,其余的都为正。

那怎么确定谁是组成([d]/2)的数呢,这一点是很有技巧的,使用位运算,二进制来看,如下

前k-1层的最左侧节点的值

1    000000000000001

2    000000000000010

4    000000000000100 ....

看到这应该知道了吧,([d]/2)&  2(^i) 如果为1,那么说明([d]/2)有2(^i),给其"-",不然就给它正号啊,那这道题不就出来了。

代码

import java.util.Scanner;

public class Solution {
	
    public static void main(String[] args) {
    	long n, k, MaxNum, d, b;
        Scanner in = new Scanner(System.in);
        n = in.nextLong();
        k = in.nextLong();
        MaxNum = 1 << k;
        d = MaxNum - n;
        b = d / 2;
        System.out.println(b);
        for(int i = 0; i < k; i++)
        {
        	if (((1<

 

你可能感兴趣的:(leetcode,java,笔试)