[路飞_js算法:二叉树的后序遍历 验证二叉树的前序序列化 基本计算器 II 表现良好的最长时间段]

168 阅读3分钟

二叉树的后序遍历

问题描述: 给定一个二叉树,返回它的 后序 遍历。(by leetcode 145

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

思路:

方法一:递归

方法二:迭代

1.遍历树节点,向压入栈中,一直找到栈顶元素的左子树,直到某一节点没有左子树,弹出该节点,再找该节点的右子树,

2.对右子树重复1操作,若没有右子树,直接返回当前节点,否则压入栈中。

3.当本右子树遍历完成,则说明本节点左右子树都已经完成,返回该节点,继续查找栈顶元素,直到栈为空。

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
方法一:递归 左右中
var postorderTraversal = function(root) {
    if(root==null) return [];
    let res=[];
    function rear(root){
        if(root==null) return;
        rear(root.left)
        rear(root.right)
        res.push(root.val)

    }
    rear(root);
    return res;
};
方法二:迭代 
var postorderTraversal = function(root) {
    if(root==null) return [];
    let res=[];
    let stk=[];
    let prev=null;
    while(root!==null||stk.length){
        while(root){
            stk.push(root);
            root=root.left
        }
       root=stk[stk.length-1];
       stk.pop();
       if(root.right==null||root.right==prev){
           res.push(root.val);
           prev=root;
           root=null;
       }else{
           stk.push(root)
           root=root.right;
       }
    }
    return res
};

验证二叉树的前序序列化

问题描述: 序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。(by leetcode 331)

     _9_
    /   \
   3     2
  / \   / \
 4   1  #  6
/ \ / \   / \
# # # #   # #

例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。

给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。

每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#'

你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3"

示例 1:

输入: "9,3,4,#,#,1,#,#,2,#,6,#,#"
输出: true

思路:

方法一:通过遍历数组,向栈中压入,遇到x##,则变为#,当遍历完最后一个值,且对栈中元素处理完。如果是二叉树,则最后应该返回只有一个#元素的栈。

方法二:通过度来判断 二叉树的话,出度-入度=0;

每个节点-1(入度),当遇到#,说明是子节点,那么+2(出度-入度=2),最后看diff的值是不是0

/**

 * @param {string} preorder
 * @return {boolean}
   */
   方法一:栈 合并“x,#,# 为 #”
   var isValidSerialization = function(preorder) {
   let arr=preorder.split(",");
   let stk=[];
   for(let i=0;i<arr.length;i++){
       stk.push(arr[i])
     while(stk.length>2&&stk[stk.length-2]==="#"&&stk[stk.length-1]==="#"&&stk[stk.length-3]!=="#"){
           stk.pop();
           stk.pop();
           stk[stk.length-1]="#"
     }
   }
   return stk.length===1&&stk[0]==="#"
   };
   方法二:度 出度-入度
var isValidSerialization = function(preorder) {
    let arr=preorder.split(",");
    let stk=[];
    let diff=1;
    for(let i=0;i<arr.length;i++){
      diff--;
     if(diff<0)return false;
     if(arr[i]!=="#")diff+=2;
    }
    return diff===0
};

基本计算器 II

问题描述: 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。(by leetcode227)

整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7

思路: 遇到数字则统计数字,遇到符号,则判断上一个符号,用栈顶元素(符号运算)当前统计的数字,并往栈中放入当前计算值,注意:最后一个元素,应该是要参与运算的。

/**
 * @param {string} s
 * @return {number}
 */
var calculate = function(s) {
    let stk=[];
    let arr= s.trim();
    let pre=0;
    let way="+"
    for(let i=0;i<arr.length;i++){
        if(!isNaN(Number(arr[i]))&& arr[i] !== ' '){
          pre=pre*10+arr[i].charCodeAt()-'0'.charCodeAt()
        }
        if(isNaN(Number(arr[i]))||i===arr.length-1){
            switch(way){ 
                case "+":
                stk.push(pre)
                break;
                case "-":
                 stk.push(-pre)
                break;
                case "*":
                 stk.push(stk.pop()*pre)
                break;
                default:
                 stk.push(stk.pop()/pre|0)
            }
            pre=0;
            way=arr[i]
        }
    }
        
    
    let res=0
    while(stk.length){
res+=stk[stk.length-1]
stk.pop()
    }
return res;
};

表现良好的最长时间段

问题描述:

给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。

我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。

所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。

请你返回「表现良好时间段」的最大长度。(by leetcode 1124)

示例 1:

输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]

思路: 看注释

/**
 * @param {number[]} hours
 * @return {number}
 */
var longestWPI = function(hours) {
  
    
    for(let i=1;i<=arr.length;i++){
        sum.push(sum[i-1]+arr[i-1])
    }
    let stack=[0]
  
    for(let i=1;i<sum.length;i++){
        if(sum[stack.length-1]>sum[i]){
            stack.push(i)
        }
    }
    let res=0;
    将每一个递减时间点跟整条增长情况折线图对比,
    while(stack.length){
        for(let i=sum.length;i>0;i--){
            if(sum[i]>sum[stack[stack.length-1]]){
                res=Math.max(i-stack[stack.length-1],res)
                
            }
    }
    stack.pop()
    }
    return res
   
};
/**
 * @param {number[]} hours
 * @return {number}
 */
var longestWPI = function(hours) {
let arr=[];、
 //1.列出时间线上劳动时间表现。
for(let i=0;i<hours.length;i++){
    if(hours[i]>8){
        arr.push(1)
    }
    else{
    arr.push(-1)
}
}
let sum=[0];
//从前完后看增长情况折线图。
for(let i=1;i<=arr.length;i++){
sum.push(sum[i-1]+arr[i-1])
}
console.log(sum)
let stack=[0];
     // 找到绝对的递减时间点
for(let i=1;i<sum.length;i++){
if(sum[i]<sum[stack.length-1]){
    stack.push(i)
}
}
console.log(stack)
let res=0;
// 针对每个绝对递减时间点,循环增长数组,有大于递减时间点的,说明这一段上面有效时间较多
while(stack.length){
    for(let i=sum.length-1;i>=0;i--){
        if(sum[i]>sum[stack.length-1]){
            res=Math.max(i-stack.length+1,res)
        }

    }
    stack.pop()
}
return res

};