[路飞]_周末看几个算法

648 阅读3分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战110. 平衡二叉树

思路

假设当前任意节点为node;
如何判断node是平衡二叉树? 是不是左侧层级与右侧层级之差小于1?
哪如何找节点node的左侧层级和右侧层级呢?
答案:递归!
递归一定有结束条件,结束条件是什么?节点为空;

下面这句话是核心:

节点为空是不是叶子节点?将节点为空表示层级为0;叶子节点的上一级是不是整个树的倒数第二层级?

在递归回退的时候,将子节点层树+1;并且是子节点左右层级的最大值+1;

然后在递归回退的时候,对比当前节点node的左右子节点层级,层级超过1的时候,整个树为非平衡树;

代码如下,细看备注

var isBalanced = function (root) {
  let sign = true //假设所有的二叉树都是平衡二叉树
  function helper(node) {
    //递归结束条件,节点为空,递归结束
    if (node === null) return 0

    // 左侧子节点层级
    let left = helper(node.left)

    //右侧子节点层级
    let right = helper(node.right)

    //计算层级差
    if (Math.abs(left - right) > 1) {
      //层级差超过1,非平衡二叉树
      sign = false
    }

    //计算,取当前节点最大层级
    return Math.max(left, right) + 1
  }
  //调用一个递归函数
  helper(root)
  return sign
}

剑指 Offer 55 - I. 二叉树的深度

看一眼就是递归呀;递归的时候记录当前走到第几层了;记录一下;当递归到最后一层;将记录的层级与其他节点层级对比找到最大值即可


var maxDepth = function (root) {
  let result = 0
  function helper(node, level) {
    // 递归结束条件
    if (node === null) {
      //递归结束,找到当前节点所在层级与最大层级较大值保存
      result = Math.max(level, result)
      return
    }

    // 左侧层级
    helper(node.left, level + 1)

    //右侧层级
    helper(node.right, level + 1)
  }

  // 递归
  helper(root, 0)
  return result
}

35. 搜索插入位置

在题目要求中看到这句话:使用时间复杂度为 O(log n) 的算法;请先把架子搭好

const len = 10;//数据长度
let left = 0;
let right = len;

while(left < right){

    // 这里考虑一下为什么是 left + (right-left)/2 而不是直接(left+right/2)
    const mid = Math.floor(left + (right-left)/2)
    
    if(mid < target){
        left = mid
    }else{
        right = mid
    }
}

本题思路也是这样,不同点是边界处理:

var searchInsert = function (nums, target) {
  let len = nums.length
  let left = 0
  let right = len - 1;
  
  // left=right时要计算一次,因为需要加入的目标值可能在left与right之间
  while (left <= right) {
    const mid = Math.floor(left + (right - left) / 2)
    if (nums[mid] === target) return mid
    if (nums[mid] < target) {
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return left
}

13. 罗马数字转整数

思路:找规律

先找到数据表达的规律; 对于罗马数字,如果左侧数值小于右侧数值;应该是右侧减去左侧;
比如:IV = 5-1 = 4; IX = 10-1 = 9 如果左侧数值大于右侧数值;应该是右侧加上左侧;
比如:VI = 5+1 = 6; LVIII = 50 + 5 + 3 = 58;

推广到整个罗马数字字符串中,对任意罗马数字从左到右有: 右侧数值大于当前数值,将当前数值减去;否则加上;

从左到右便利一次字符串即可转换为阿拉伯数字

var romanToInt = function (s) {
  const map = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }
  const len = s.length
  let result = 0
  for (let i = 0; i < len; i++) {
    const c = map[s[i]]
    if (i < len - 1 && map[s[i]] < map[s[i + 1]]) {
      result -= map[s[i]]
    } else {
      result += map[s[i]]
    }
  }
  return result
}

344. 反转字符串

正常解一行代码搞定,但是题目有要求:必须原地修改输入数组并且使用 O(1) 的额外空间解决这一问题;

直接左右调换即可;数组,可以用两个指针分别只想数组头尾;依次相互调换即可

var reverseString = function (s) {
  const len = s.length
  let left = 0
  let right = len - 1
  while (left < right) {
    const t = s[left]
    s[left] = s[right]
    s[right] = t
    left++
    right--
  }
  return s
}