持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情
今日的LeetCode每日一题是779. 第K个语法符号 - 力扣(LeetCode)。其实一开始以为是一道模拟问题,但是实际上是一道关于二叉树的问题。
题目内容
我们构建了一个包含 n
行( 索引从 1 开始 )的表。首先在第一行我们写上一个0
。接下来的每一行,将前一行中的0
替换为01
,1
替换为10
。
- 例如,对于
n = 3
,第1
行是0
,第2
行是01
,第3
行是0110
。
问:给定行数
n
和序数k
,返回第n
行中第k
个字符。(k 从索引 1 开始)
样例
输入: n = 1, k = 1
输出: 0
解释: 第一行:0
输入: n = 2, k = 1
输出: 0
解释:
第一行: 0
第二行: 01
输入: n = 2, k = 2
输出: 1
解释:
第一行: 0
第二行: 01
方法
这道题如果读完很容易就可以想到来模拟上述生成字符的逻辑,就可以得到最终的结果,但是它存在的问题是需要过多的空间复杂度,因此我们可以换一个思路来求解这个问题,比如而不需要利用模拟来申请过多的存储空间。
根据题目的内容, 在生成字符过程中:
- 初始的第一个字符是
0
0
会变成01
1
会变成10
上述过程反复迭代,直到可以得到最终的n
和k
,上述的过程画成树
的形式,具体如下图所示。不难发现它是一种满二叉树
满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。
从树的角度来思考这道题,计算第n
行中第k
个字符就变成了计算满二叉树中第n
层的第k
个叶子,
而该叶子是由它的父节点计算得出。
那么如何确定左右节点呢?
- 如果父节点是
0
,则左子节点的值是0
,右节点的值是1
- 如果父节点是
1
,则左子节点的值是1
,右节点的值是0
具体可以采用^
运算进行计算,^
运算是相同为0
,不同为1
,:
- 偶数是右节点:
type = 0
, 如果父节点为0,则右节点是0,如果父节点为1,则右节点为1 - 奇数是左节点:
type = 1
,如果父节点为1,则左节点是1,如果父节点为0,则左节点为1
那么如何确定父节点呢?
- 确定左右节点:因为求解的是第
n
行的第k
个节点,假设第一个节点的位置是1
,则可以发现偶数为右节点,奇数为左节点 - 确定父节点:从图中可以看出,第
3
行1
和2
节点的父节点是1
,3
和4
的父节点是2
,则第n
行的第k
个节点父节点是第n - 1
行的(k + 1) / 2
那么可以将整个过程总结下来代码如下:
public int kthGrammar(int n, int k) {
if (n == 1){
return 0;
}
// 左边为1,右边为0
int type;
if (k % 2 == 1){
type = 0;
}else{
type = 1;
}
int father = kthGrammar(n - 1, (k + 1) / 2);
return type ^ father;
}