题目来源:https://leetcode-cn.com/problems/path-in-zigzag-labelled-binary-tree/
大致题意:
给定一个无限大的二叉树,该二叉树的奇数行和正常的完全二叉树一样,节点从左到右依次递增;而偶数行则顺序相反。
然后对于给出的一个 label,找出从根节点到 label 的路径。
思路
对于正常的二叉树节点 num 来说,其父节点的值为 num/2。
而该二叉树顺序因行的奇偶性不同而不同,所以需要加上特判。
可以注意到,对于正常的完全二叉树,也就是该二叉树的奇数行:
- 若行数为 i,则该行左端元素为 2^(i-1)
右端元素为2^i - 1
而对于偶数行,有
- 若行数为 i,则该行右端元素为 2^(i-1)
左端元素为2^i - 1
即,将节点顺序左右颠倒。
若将正常的顺序认定为正确位置,那么将偶数行的节点颠倒可得到正确位置。
设元素位置从左至右为x,那么同一元素颠倒前后位置和有如下关系:
- 变换前后位置关于中位数对称,也就是说 前后位置和为 2^(该层数 - 1)
- 变换前后的值和为 该层左右端元素和,即2^(i-1) + 2^i - 1
于是可得变换值为
- 变换前值为x,变换后为 2^(i-1) + 2^i - 1 - x
于是,对于给定的label,可有如下判断
- 先判断当前行是否为偶数行
- 若为偶数行,求出位置对应的变换后的数
- 若为奇数行,当前数(位置)即为遍历到的数
而初始时,需要找出label所在行数,然后若其为偶数行,需要先预处理。
代码:
public List<Integer> pathInZigZagTree(int label) {
int row = 1;
int rowStart = 1;
while (rowStart * 2 <= label) {
row++;
rowStart *= 2;
}
if (row % 2 == 0) { // 初始化
// 因为label值本身要存入列表
// 而若第一次对label处理时,当前行为偶数,转换后的值就不为label本身
label = reverseNum(row, label);
}
List<Integer> path = new ArrayList<Integer>();
while (row > 0) {
// 若为偶数行,转换值
// 注意第一次时在这里又转换一次
if (row % 2 == 0)
path.add(reverseNum(row, label));
else
path.add(label);
row--;
label >>= 1;
}
Collections.reverse(path);
return path;
}
public int reverseNum(int row, int label) { // 转换值
return 1 << (row-1) + (1 << row) - 1 - label;
}