【每日一题】:779 第K个语法符号

31 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

今日的LeetCode每日一题是779. 第K个语法符号 - 力扣(LeetCode)。其实一开始以为是一道模拟问题,但是实际上是一道关于二叉树的问题。

题目内容

我们构建了一个包含 n 行( 索引从 1 开始 )的表。首先在第一行我们写上一个0。接下来的每一行,将前一行中的0替换为011替换为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

上述过程反复迭代,直到可以得到最终的nk,上述的过程画成的形式,具体如下图所示。不难发现它是一种满二叉树

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。

Untitled-2022-10-13-0030.png

从树的角度来思考这道题,计算第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,则可以发现偶数为右节点,奇数为左节点
  • 确定父节点:从图中可以看出,第312节点的父节点是134的父节点是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;
    }