给定这么一个二叉树, 求解 (1) 对于给定正整数 n, 求解其值, 比如当 n = 5 时, 返回 p = 3, q = 2 (2) 对于给定的 p,q 返回其序号 n
思路
1. 最初以为这是道数学题, 但当画出第四层叶子节点时, 发现即便这是道数学问题, 也可以用递归求解. 同时因为此题属于完全二叉树, 所以使用递归效率也不会太差, 毕竟每次递归数据规模都可以减少一半
2. 已知 n 或已知 p,q 都属于某一个叶子节点, 因此我们需要从底向上推导, 属于 bottomUp 算法. 我曾总结过 bottomUp 算法, 那次的结论是使用后序遍历即可, 但这是一道逻辑上的树, 不能进行后续遍历, 所以需要另想方法
3. 对于第一问, 已知 n, 通过手工计算的话, 是根据 n 的奇偶性选择向上走的方向, 一直走到 root, 记录路径再回溯回来. 用递归实现, 有点意思, 这里我还想了蛮长时间, 毕竟第一次接触, 感觉有点绕, 最后写出这样的代码
void bottomUp(unsigned long long n, unsigned long long &p, unsigned long long &q) { if(n == 1) { p = 1; q = 1; return; } // n != 1; bottomUp(n/2, p, q); if(n & 1) { // odd number p = p+q; }else{ q = p+q; } }
代码的精髓在于使用一个函数来做出了 bottomUp and 回溯回去两个过程
4. 第 2 问就显得更加直接了, 思路相同
void nth(unsigned long long p, unsigned long long q, unsigned long long &n) { if(p == 1 && q == 1) { n = 1; return; } if(p > q) { unsigned long long newp = p-q; nth(newp, q, n); n = n*2 +1; }else{ unsigned long long newq = q-p; nth(p, newq, n); n *= 2; } }
想到 Leetcode 上一道题, 求解十进制数中 1 的个数, 递归求法同样奇妙
代码 未通过 JOBDU 测试
#include <utility> #include <iostream> #include <math.h> using namespace std; void bottomUp(unsigned long long n, unsigned long long &p, unsigned long long &q) { if(n == 1) { p = 1; q = 1; return; } // n != 1; bottomUp(n/2, p, q); if(n & 1) { // odd number p = p+q; }else{ q = p+q; } } void nth(unsigned long long p, unsigned long long q, unsigned long long &n) { if(p == 1 && q == 1) { n = 1; return; } if(p > q) { unsigned long long newp = p-q; nth(newp, q, n); n = n*2 +1; }else{ unsigned long long newq = q-p; nth(p, newq, n); n *= 2; } } int main() { //freopen("testcase.txt", "r", stdin); unsigned long long n, p, q; unsigned long long cases, types; while(scanf("%lld", &cases) != EOF) { for(int i = 0; i < cases; i ++) { scanf("%lld", &types); n = p = q = 0; if(types == 1) { scanf("%lld", &n); bottomUp(n, p, q); printf("%lld %lld\n",p, q); }else{ scanf("%lld%lld", &p, &q); nth(p, q, n); printf("%lld\n",n ); } } } return 0; }