779. 第K个语法符号
在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为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
注意:
N的范围[1, 30].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
};