算法打卡第二十天

112 阅读3分钟
  1. 剑指 Offer 07. 重建二叉树
  2. 剑指 Offer 16. 数值的整数次方
  3. 剑指 Offer 33. 二叉搜索树的后序遍历序列

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

提议理解: 根据二叉树的先序遍历和中序遍历,推出二叉树 先序遍历 根左右 中序遍历 左根右 先序和中序的相似出拆分

  1. 如果只考虑左子树,则先序和中序的顺序互反
  2. 只考虑右子树,则先序和中序顺序相同
  3. 根据次特点,可以使用递推进行运算,先序遍历的第一个数就是根节点
  4. 使用栈进行辅助计算stack,根节点为root

preorder = [3, 9, 1, 2, 20, 15, 7] inorder = [1, 9, 2, 3, 15, 20, 7] 首先可以确定根节点为3,设先序遍历当前索引为i 开始从1开始,0已确定为头结点,后序遍历当前索引为rightIndex 如果preorder[i]!== inorder[rightIndex], 则preorder[i]为root的左节点,将节点preorder[i]存入栈中 否则preorder[i]就是某个父级或祖先级节点的右节点,栈中无数据则为根据2直接为右节点;栈中有数据则回溯父件点,根据1,rightIndex++,执行出栈操作, 找到不同处则为该节点的右节点

var buildTree = function(preorder, inorder) {
    if (!preorder.length || !inorder.length) {
        return null
    }
    let root = new TreeNode(preorder[0])
    let stack = [];
    stack.push(root);
    let rightIndex = 0;
    for (let i = 1; i < preorder.length; i++) {
        let pre = preorder[i];
        let node = stack[stack.length - 1] || null;
        if (node.val !== inorder[rightIndex]) {
            node.left = new TreeNode(pre)
            stack.push(node.left)
        } else {
            while (stack.length && stack[stack.length - 1].val === inorder[rightIndex]) {
                node = stack.pop();
                rightIndex++;
            }
            node.right = new TreeNode(pre)
            stack.push(node.right)
        }
    }
};

剑指 Offer 16. 数值的整数次方

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

题意理解: 计算x的n次方,不使用函数库 二分+递归

  1. 特殊情况处理,x为0直接返回0,n为0直接返回1,n为1返回x,n为-1返回倒数
  2. 判断n的奇偶性,偶数次直接拆;奇数次向下取整,多乘一次x

提示: -100.0 < x < 100.0 -231 <= n <= 231-1 -104 <= xn <= 104

var myPow = function(x, n) {
    if (x === 0) return 0;
    if (n === 0) return 1;
    if (n === 1) return x;
    if (n === -1) return 1 / x;
    if (n % 2 === 0) {
        let a = myPow(x, n / 2);
        return a * a
    } else {
        let b = myPow(x, (n - 1) / 2);
        return b * b * x
    }
};

剑指 Offer 33. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

提议理解: 输入一个数组判断是否为二叉搜索树的后序遍历结果 二叉搜索树,左小于根,右大于根 利用后序遍历 左 右 根 他的倒叙为根 右 左 输入的数组最后一个数为根节点

  1. 初始化root用来存储当前的右节点,初始化为一个大数
  2. 栈用来存储右节点和根节点
  3. 出栈操作完成root永远存的是当前以遍历的最大右节点
  4. 当栈中有数据,且栈顶元素大于当前元素,则进行出栈操作,root=出栈数
  5. 当前数大于栈顶数据时,表示此节点不是左节点,进行入栈操作
  6. 当出现root小于当前遍历的节点时,返回false

有图更好看

var verifyPostorder = function(postorder) {
    const stack = [];
    let root = Number.MAX_VALUE;
    for (let i = postorder.length - 1; i >= 0; i--) {
        if (postorder[i] > root) return false;
        while (stack.length && stack[stack.length - 1] > postorder[i]) {
            root = stack.pop();
        }
        stack.push(postorder[i]);
    }
    return true;
}

递推

  1. 输入的数组最后一个数为根节点
  2. 根据这个进行数组拆分,拆分成若干
  3. 索引左和右,当左大于等于有索引是返回true
  4. 左节点都小于根节点,右节点都大于根节点
  5. 采用累加进行判断,即做到第一个大于节点的数,此数左边都是左节点进行索引累加,剩下的大于根节点的索引累加
  6. 如果累加后索引不等于根节点的索引,则不满足条件返回false
var verifyPostorder = function(postorder) {
    function recur(postorder, l, r) {
        if (l >= r) return true;
        let left = l;
        while (postorder[left] < postorder[r]) {
            left++;
        }
        let m = left;
        while (postorder[left] > postorder[r]) {
            left++;
        }
        return p == r && recur(postorder, l, m - 1) && recur(postorder, m, r - 1);
    }
    return recur(postorder, 0, postorder.length - 1)
}