38. 外观数列
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如,数字字符串 "3322251"
的描述如下图:
思路:
- 运用递归思想,通过不断获取countAndSay(n-1)得到的结果来计算当前n的结果,把问题分割成多个小问题。
- 控制变量i的变化,从第二次循环开始,i应该第一个跟last[i]不相等的数字的下标,即i=j,比如在“11221”中,进入第二次循环时,i=2;第三次循环,i=4。
- 默认变量count为1,最后一齐把count和last[i]一齐push进sb数组里,在通过通过sb.join('')把结果变为字符串。
代码:
var countAndSay = function(n) {
if (n == 1) return "1"
let sb=[]
let last=countAndSay(n-1)
for(let i=0;i<last.length;){
let count=1;
for(j=i+1;j<last.length;j++){
if(last[i]==last[j]){
count++;
}
else{
break;
}
}
sb.push(''+count+last[i])
i=j
}
sb=sb.join('')
return sb
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
思路:
二叉搜索树是left < root < right的,后序遍历的顺序是left->right->root,乍一看,好像没有办法保证单调性,不过我们可以做一个变化,后序遍历的逆序:
root->right->left
发现什么了吗?是的,这是换了一个方向的先序遍历,从root开始,先遍历右子树,再遍历左子树。怎么做到先root,然后right,最后left呢,只要我们反向遍历数组,这样我们就可以利用单调栈了。
- 下面说说单调递增栈的思路和用法。
翻转先序遍历又是root->right->left的,基于这样的性质和遍历方式,我们知道越往右越大,这样,就可以构造一个单调递增的栈,来记录遍历的元素。
为什么要用单调栈呢,因为往右子树遍历的过程,value是越来越大的,一旦出现了value小于栈顶元素value的时候,就表示要开始进入左子树了(如果不是,就应该继续进入右子树,否则不满足二叉搜索树的定义,不理解的请看下二叉搜索树的定义),但是这个左子树是从哪个节点开始的呢?
单调栈帮我们记录了这些节点,只要栈顶元素还比当前节点大,就表示还是右子树,要移除,因为我们要找到这个左孩子节点直接连接的父节点,也就是找到这个子树的根,只要栈顶元素还大于当前节点,就要一直弹出,直到栈顶元素小于节点,或者栈为空。栈顶的上一个元素就是子树节点的根。
接下来,数组继续往前遍历,之后的左子树的每个节点,都要比子树的根要小,才能满足二叉搜索树,否则就不是二叉搜索树。
代码:
var verifyPostorder = function(postorder) {
var root = Number.MAX_VALUE
var stack = []
for(let i = postorder.length - 1;i >= 0;i--)
{
if(postorder[i] > root) return false
while(stack.length && postorder[i] < stack[stack.length -1])
{
root = stack.pop()
}
stack.push(postorder[i])
}
return true
};