[路飞]_程序员必刷力扣题: 779. 第K个语法符号

·  阅读 152
[路飞]_程序员必刷力扣题: 779. 第K个语法符号

779. 第K个语法符号

在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为011替换为10

给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)。

示例 :

输入: N = 1, K = 1
输出: 0

输入: N = 2, K = 1
输出: 0

输入: N = 2, K = 2
输出: 1

输入: N = 4, K = 5
输出: 1

解释:
第一行: 0
第二行: 01
第三行: 0110
第四行: 01101001

复制代码

注意:

  1. N 的范围 [1, 30].
  2. K 的范围 [1, 2^(N-1)].

获取操作栈

思路

由题可得,我们从第一行0开始,每新增一行则按照题目规则替换上一行中的0和1

  • 暴力求解
    • 按照题目要要求,生成每一行的字符串,然后直接获取第k个字符

问题:

因为每一行字符串的长度为上一行的2倍,字符串的增长速度为2的n次方,字符串的增长数度会非常快,导致速度非常慢

  • 获取操作栈

观察题意,我们可以发现,题目其实是一个二叉树的结构,0位根节点,子节点分别为[0,1];接着往下0的子节点为[0,1],1的子节点为[1,0]...

当我们要求得第n行,第k个值的时候。如果能够知道他的父节点的值,以及当前属于父节点的左子树还是右子树,我们就能从父节点推出当前节点的值(例:父节点为0,当前节点为父节点的左子树,那么当前值为0),同理,我们只需要继续往上推算,直到根节点,我们就能跟据每一步的操作,逐渐向下得到第n行的第k个节点的值了

那么该如何判断当前第k个值属于左子树还是右子树呢?

这里其实只要跟据,k的奇偶就可以知道,奇数为左子树,偶数为右子树

如何判断父节点属于上一行中的第几位呢?

题目中字符串的增速为2的n次方,当前节点的父节点就是上一行中的第Math.ceil(k / 2)位,如果k为奇数,那么父节点就是第(k+1)/2位,如果k为偶数,父节点就是第k/2位,所以只需要对k/2向上取整,就能知道父节点在上一行中的位置

得到父节点的位置后,怎么知道父节点属于爷爷节点的左子树还是右子树呢?

同第一步

......

同理往上,当我们得到第2行时,我们知道第二行的父节点必然为0,再知道属于做还是右子树,就能确定当前行的值,同理我们有完整的操作栈后,就可以递推出第n行第k个值了

代码如下:

我们先遍历n,n逐渐递减,跟据以上规则,我们用[n,k,pos]([第n行,第k个元素,左子树还是右子树(左为0,右为1)])来表示当前行需要的信息,并push到treeStack,得到左右的操作栈

然后遍历操作栈,用curr表示父节点的值,跟据左右子树位置得到下一行的值,并更新curr继续往下,最后更新的curr就是我们需要的值

返回curr

var kthGrammar = function (n, k) {
    if (n === 1 && k === 1) return '0'
    var treeStack = []
    while (n > 1) {
        var pos = k % 2 === 0 ? 1 : 0
        var opt = [n, k, pos]
        k = Math.ceil(k / 2)
        treeStack.push(opt)
        n--
    }
    var curr = '0'
    while (treeStack.length) {
        var item = treeStack.pop()
        var newCurr = curr === '1' ? '10' : '01'
        var index = item[2]
        curr = newCurr[index]
    }
    return curr
};
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改