面试题:算法

一、数组
二、字符串
三、链表(大多数组的思维可解)
删除:可利用找要删除的上一个进行 next.next或将当前保存成下一个在进行移除下一个
反转:可利用建立新的节点 原 head.next.next=head head.next=null
四:二叉树
(1)前序遍历:根左右
(2)中序遍历:左根右
(3)后序遍历:左右根
(4)层序遍历:一层层(封装一个每个节点遍历的方法)

// 前序后序中序的思路:按先后遍历的顺序递归
var inorderTraversal = function(root) { // 中序遍历:左根右
    if(root){
        return [...inorderTraversal(root.left),root.val,
        ...inorderTraversal(root.right)]
    }else{
        return []
    }
};
// [1,null,2,3] [1,3,2]

// 层序遍历(动态规划:保存上一层的依次遍历)
var levelOrder = function(root) {
    if(!root) {
        return []
    }
    let res = []
    dfs(root, 0) // 当前遍历到的节点、层数
    return res

    function dfs(root, index){//每层透传当前节点,层数,和输出数组
        if(root){
            if(!res[index]) res[index] = [] //初始化当前层数组
            res[index].push(root.val)//当前节点加入当前层数组
            dfs(root.left, index + 1)//index+1,递归左节点	
            dfs(root.right, index + 1)//index+1,递归右节点	
        }   
    }
};

(5)相同的树(利用自己递归,特殊一般情况考虑)
(6)对称二叉树(递归,左的左=右的右,自己定一个一层层判断左右是否相同)
(7)平衡二叉树(定义一个求左右子树高的函数,每个节点判断是否是平衡二叉树)
(8)翻转二叉树(递归左右,左右交换)

// 相同的树(左右都同,递归&&的关系)
  var isSameTree = function(p, q) { // 完全相同节点的值及左右都同
	if(p===null&&q===null){
	    return true
	}
	if(p===null&&q!==null){
	    return false
	}
	if(p!==null&&q===null){
	    return false
	}
    if(p.val!==q.val){
      return false
    }
    return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right)
 };
 
// 对称二叉树(一棵树,每个节点的左=右) 写一个函数判断两函数是否相等
var isSymmetric = function(root) {
    if(root===null){ // 空树
        return true
    }
    return backFun(root.left,root.right)
    function backFun(left,right){
        // 还要进行特殊情况考虑,万一是空节点呢
        if(left===null&&right===null){
            return true
        }
        if(left===null&&right!==null){
            return false
        }
        if(left!==null&&right===null){
            return false
        }
        if(left.val!==right.val){
            return false
        }
        return backFun(left.left,right.right)&&backFun(left.right,right.left)
    }
};

// 平衡二叉树(一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1)
var isBalanced = function(root) { // 统计每个节点的左子树及右子树的高之差
   if(root===null){ // 空树
       return true
   }
   // 判断每个节点的最高深度
   function maxDeep(node){
       if(node===null){
           return true
       }
       let left=maxDeep(node.left)
       let right=maxDeep(node.right)
       return Math.max(left,right)+1 // 跟节点也要计算
   }
   if(Math.abs(maxDeep(root.left)-maxDeep(root.right))>1){
        return false
    }else{
        return isBalanced(root.left)&&isBalanced(root.right)
    }
};

// 翻转二叉树也是镜像二叉树(递归拿到左子树和右子树存值,互换)
var invertTree = function(root) { // 每个节点的左右都交换
   if(root===null){
       return null
   }
   let left=invertTree(root.left) // 左树
   let right=invertTree(root.right)
   root.left=right
   root.right=left
   return root
};

(9)二叉树的最大深度
(10)二叉树的最小深度(要特殊情况考虑,因为取小的,只有左右都为null才算叶子节点)

// 二叉树的最大深度(只统计一次,找左右大者,每一层加一)
var maxDepth = function(root) {
    if(root===null){
        return 0
    }
    let leftDeep=maxDepth(root.left)
    let rightDeep=maxDepth(root.right)
    let deep=leftDeep>rightDeep?leftDeep:rightDeep
    return deep+1
};

// 二叉树的最小深度
var minDepth = function(root) {
    if(root===null){
        return 0
    }
    if(root.left===null&&root.right===null){
        return 1
    }
    if(root.left===null&&root.right!==null){
        return minDepth(root.right)+1
    }
    if(root.right===null&&root.left!==null){
        return minDepth(root.left)+1
    }
    return  Math.min(minDepth(root.left),minDepth(root.right))+1
};

(11)将有序数组转为二叉搜索树(升序数组转为高度平衡的二叉搜索树)
(12)路径总和(判断根到叶子节点有无等于目标值的路径)
(13)二叉树的所有路径(递归:把它指向成左右根的连接)
(14)左叶子之和

// 将有序数组转为高度平衡的二叉搜索树(注意中间根节点不能重复)
var sortedArrayToBST = function(nums) { // 注意返回的是树形结构
    // 特殊情况考虑
    if(nums.length===0){
        return null
    }
    if(nums.length===1){
        return new TreeNode(nums[0])
    }
    let mid=parseInt(nums.length/2)
    let root=new TreeNode(nums[mid])
    root.left=sortedArrayToBST(nums.slice(0,mid))
    root.right=sortedArrayToBST(nums.slice(mid+1))
    return root
};

// 路径总和I(每遍历一层减去当前的值,遇到叶子节点判断是否为所需要值)
var hasPathSum = function(root, targetSum) {
    // 思路:如果碰到叶子节点求一次判断
    if(root===null){ // 空树
        return false
    }
    if(root.left===null&&root.right===null){ // 是叶子节点
       return root.val===targetSum
    }
    return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val)
};

// 二叉树的左右路径
var binaryTreePaths = function(root) {
    if(root===null){
        return []
    }
    if(root.left===null&&root.right===null){// 只有根节点
       return [root.val.toString()]
    }
    // 一般情况
    let left=binaryTreePaths(root.left)
    let right=binaryTreePaths(root.right)
    return left.concat(right).map((item)=>root.val+'->'+item)
};

// 左叶子之和(公共函数,每个节点的左是叶子节点)
var sumOfLeftLeaves = function(root) { // 左叶子指:它是做节点但自己没有左右节点
// 每个节点的左且是叶子节点
if(root===null){ // 空树
    return 0
}
if(root.left===null&&root.right===null){ // 只有 根节点
  return 0
}
 let result=0
 backFun(root)
 return result
 function backFun(root){
     if(root===null){
         return
     }
     if(root.left!==null&&root.left.right===null&&root.left.left===null){
         result+=root.left.val
     }
     backFun(root.left)
     backFun(root.right)
 }
 return result
};

(15)二叉搜索树中的众数(出现频率最高的,可能存在多个,按任意顺序数组返回)
(16)二叉搜索树中最小绝对差
(17)二叉树的直径
(18)二叉树的坡度

// 二叉搜索树中的众数(写一个函数遍历并将值存放在对象中)
var findMode = function(root) {
   let obj=new Map()
   function backFun(root){
       if(!root){ // null值
          return
       }
       if(obj[root.val]){
           obj[root.val]++
       }else{
           obj[root.val]=1
       }
       backFun(root.left)
       backFun(root.right)
   }
    backFun(root)
    let max=0,result=[]
    // 从对象中判断众数
    for(let key in obj){
        max=Math.max(Number(obj[key]),max)
    }
    for(let key in obj){
       if(Number(obj[key])===max){
           result.push(Number(key))
       }
    }
  return result
};

// 二叉搜索树中最小绝对差(定义函数遍历所有节点值到数组,根据数组双循环拿到)
var getMinimumDifference = function(root) {
    // 思路将节点值都拿出来,然后 数组动态规划判断
    let item=[]
    function backFun(root){
        if(!root){
            return
        }
        item.push(root.val)
        backFun(root.left)
        backFun(root.right)
    }
    backFun(root)
    // 数组中最小绝对值差值
    let minNumber=Number.MAX_VALUE
    for(let i=0;i<item.length;i++){
        for(let j=i+1;j<item.length;j++){
            let cur=Math.abs(item[i]-item[j])
            minNumber=Math.min(minNumber,cur)
        }
    }
    return minNumber
};

// 二叉树直径
var diameterOfBinaryTree = function(root) {
    let res = 0
    depth(root)
    return res
    function depth (node) { // 返回子树的深度
        if (!node) return 0 // 节点不存在返回0
        let left = depth(node.left) // left为左子树的深度
        let right = depth(node.right)//right 为右子树的深度
        res = Math.max(left + right, res) //计算l+r 更新res
        return Math.max(left, right)+1 //返回该节点为根的子树的深度
    }
};


// 二叉树坡度()
var findTilt = function(root) {
    // 累加整个坡度。定义一个函数求该节点的坡度 (左右差的绝对值)
    let total=0
    backFun(root)
    return total
    function backFun(root){
        if(!root){
            return 0
        }
        let left=backFun(root.left)
        let right=backFun(root.right)
        total+=Math.abs(left-right)
        return left+right+root.val
    }
};

(19)另一颗树的子树
(20)根据二叉树创建字符串
(21)合并二叉树
(22)二叉树的层平均值

// 另一颗树的子树(转成字符串进行判断是否包含)
var isSubtree = function(root, subRoot) {
    return JSON.stringify(root).includes(JSON.stringify(subRoot))
};

// 根据二叉树创建字符串(递归前序遍历实现)
var tree2str = function(root) { // 前序遍历 跟左右,根据题意右为空括号可以删除
  if(!root){  // 空树
     return ''
  }
  if(root.left===null&&root.right===null){ // 只有跟节点
      return `${root.val}`
  }
  if(root.left===null&&root.right!==null){ // 左 子树 为空
    return `${root.val}()(${tree2str(root.right)})`
  }
  if(root.right===null&root.left!==null){
      return `${root.val}(${tree2str(root.left)})`
  }
  // 正常左右都存在
  return `${root.val}(${tree2str(root.left)})(${tree2str(root.right)})`
};

// 合并二叉树(只更新,值,左、右,将新的放在其中一个树中,更新)
var mergeTrees = function(root1, root2) {
    // 特殊情况。
    if(root1===null||root2===null){
        return root1||root2
    }
    // 一般情况考虑 左右子树都存在,将合并到1上(更新值及左右)
    root1.val=root1.val+root2.val
    root1.left=mergeTrees(root1.left,root2.left)
    root1.right=mergeTrees(root1.right,root2.right)
    return root1
};

// 二叉树的层平均值(先层级编辑再求平均值)
var averageOfLevels = function(root) {
    // 先进性层序遍历取值,在进行 求平均值
    let  res=[]
    average(root,0)
    function average(root,index){
        if(root){
            if(!res[index]){ // 初始化层级数组
                res[index]=[]
            }
            res[index].push(root.val)
            average(root.left,index+1)
            average(root.right,index+1)
        }
    }
   let result=[]
   for(let i=0;i<res.length;i++){ // 求每项总和的平均值
     let sum=res[i].reduce((a,b)=>a+b)
     let ave=sum/(res[i].length)
     result.push(ave)
   }
   return result
};

(23)两数之和IV(二叉搜索树中存在两数之和为目标值)
(24)二叉树中第二小的节点
(25)二叉搜索树中的搜索
(26)二叉搜索树中节点最小距离

// 两数之和(思路:写函数前序遍历,只拿有值,在进行遍历找值)
var findTarget = function(root, k) { // 思路前序遍历在遍历 求值
    let result=[]
    function iterator1(root){
       if(root){
           return [root.val,...iterator1(root.left),...iterator1(root.right)]
        }else{
            return []
        }
    }
    result=iterator1(root)
    for(let i=0;i<result.length;i++){
        for(let j=i+1;j<result.length;j++){
            if(result[i]+result[j]===k){
                return true
                break
            }
        }
    }
  return false
};

// 二叉树中第二小的节点
var findSecondMinimumValue = function(root) {
    let result=[]
    function backFun(root){
        if(root){
            return [root.val,...backFun(root.left),...backFun(root.right)]
        }else{
            return []
        }
    }
    result=backFun(root)
    result=Array.from(new Set(result))
    if(result.length<2){
        return -1
    }else{
        result.sort((a,b)=>a-b)
        return result[1]
    }
};

// 二叉搜索树中的搜索(实现找出目标节点的子树,根据搜索树的特性进行值大小怎么遍历判断)
var searchBST = function(root, val) { // 实质遍历找到相同的 值,返回该节点
  // 特殊条件判断
  if(!root){
      return null
  }
  if(root.val===val){
      return root
  }
  // 一般情况判断
  return searchBST(val>root.val?root.right:root.left,val)
};

// 二叉搜索树中节点最小距离(任意两节点最小绝对差。遍历去重找最小差值)
var minDiffInBST = function(root) {
    let result=[]
    function backFun(root){
        if(!root){
            return []
        }else{
            return [root.val,...backFun(root.left),...backFun(root.right)]
        }
    }
    result=backFun(root)
    // 找绝对值最小的
    let min=Number.MAX_VALUE
    for(let i=0;i<result.length;i++){
        for(let j=i+1;j<result.length;j++){
            let cur=Math.abs(result[i]-result[j])
            min=Math.min(min,cur)
        }
    }
   return min
};

(28)叶子相似的树
(29)递增顺序搜索树
(30)二叉搜索树的范围和
(31)单值二叉树

// 叶子相似的树(两树拿到的叶子节点同)
var leafSimilar = function(root1, root2) { // 遍历叶子节点,从左到右塞入数组
// 分别后续遍历叶子节点
let result1=[],result2=[]
function backFun(root,arr){
    if(!root){ // 空节点
        return
    }
    if(root.left===null&&root.right===null){ // 是叶子 节点
        arr.push(root.val)
    }
    backFun(root.left,arr)
    backFun(root.right,arr)
}
    backFun(root1,result1)
    backFun(root2,result2)
    return result1.join()===result2.join()
};

// 递增顺序搜索树(按中序遍历使最左是跟,每个节点只有右节点的递增树)
var increasingBST = function(root) { // 中序遍历所有的节点然后一次连接树(光右节点)
  let result=[] // 中序遍历的数组
  function backFun(root){
      if(root){
          backFun(root.left)
          result.push(root.val)
          backFun(root.right)
      }
  }
  backFun(root)
  let newRoot=new TreeNode(0) // 新的节点 
  let cur=newRoot
  for(let i=0;i<result.length;i++){
      cur.right=new TreeNode(result[i])
      cur=cur.right
  }
  return newRoot.right
};

// 二叉搜索树的范围和(给出范围求在范围内的节点值和:先树遍历再求和)
var rangeSumBST = function(root, low, high) { // 包含临界值
 // 思路先遍历
 let result=[]
  function backFun(root){
      if(root){
          if(root.val>=low&&root.val<=high){
              result.push(root.val)
          }
          backFun(root.left)
          backFun(root.right)
      }
  }
  backFun(root)
  let sum=result.reduce((a,b)=>a+b)
  return sum
};

// 单值二叉树(所有节点值都相同)
var isUnivalTree = function(root) { // 思路:遍历过滤后只有一项
   let result=[]
   function backFun(root){
       if(root){
           result.push(root.val)
           backFun(root.left)
           backFun(root.right)
       }
   }
   backFun(root)
   let arr=Array.from(new Set(result))
   return arr.length===1
};

(32)二叉树的堂兄弟节点
(33)从根到叶的二进制数之和
(34)找出克隆二叉树中的相同节点
(35)判断根结点是否等于子结点之和
(36)开幕式焰火

// 二叉树的堂兄弟节点(同一层级但父不同:层序遍历存储)
var isCousins = function(root, x, y) { // 利用层序遍历存储层和父节点 
  const getHeight = (root, prev,  height) => {
        if (!root) {
            return 
        }
        if (root.val === x) {
            first.parent = prev
            first.height = height
        } 
        if(root.val===y){
            second.parent = prev
            second.height = height
        }
        // 递归左子树
        getHeight(root.left, root,  height + 1)
        // 递归右子树
        getHeight(root.right, root,  height + 1)
    }
    let first = {}
    let second = {}
    getHeight(root, null, 0)
    return first.height === second.height && first.parent !== second.parent
};

// 从根到叶的二进制数之和(难点:都要从根出发)
var sumRootToLeaf = function(root) {
   let res=0 // 总的求和
   const backFun=(root,sum)=>{
       let tem=(sum<<1)+root.val  // 二进制相加
       if(root.left===null&&root.right===null){ // 到达叶子节点(存储当前分支答案)
           res+=tem
           return 
       }
       // 没到叶子节点,继续往下遍历
       if(root.left){
           backFun(root.left,tem)
       }
       if(root.right){
           backFun(root.right,tem)
       }
   }
   backFun(root,0)
   return res
};

// 找出克隆二叉树中的相同节点 (结构相同,同时遍历)
var getTargetCopy = function(original, cloned, target) {
    // 思路遍历副本,找到值相同的节点 
    // 特殊情况考虑
    if(!original||!cloned){
        return null
    }
    if(original===target){
        return cloned
    }
    return getTargetCopy(original.left,cloned.left,target)||getTargetCopy(original.right,cloned.right,target)
};
// 判断根结点是否等于子结点之和(根的值=左+右)
var checkTree = function(root) {
    return root.val===root.left.val+root.right.val
};

// 开幕式焰火
var numColor = function(root) {
    // 思路:遍历统计  或者先遍历出来去重 利用前序遍历 Set数据结构去重
    let obj=new Map()
    const backFun=(root)=>{
        if(root){
            if(obj[root.val]){
                obj[root.val]++
            }else{
                obj[root.val]=1
            }
            backFun(root.left)
            backFun(root.right)
        }
    }
    backFun(root)
    return Object.keys(obj).length
};

(37)二叉搜索树的最近公共祖先(给出其中两个节点,找最近的公共祖先)利用搜索树特性

// 二叉搜索树的最近公共祖先
	//如果 p 和 q 都比当前节点小,那么显然 p 和 q 都在左子树,那么 LCA 在左子树
	//如果 p 和 q 都比当前节点大,那么显然 p 和 q 都在右子树,那么 LCA 在右子树
	//一旦发现 p 和 q 在当前节点的两侧,说明当前节点就是 LCA
	var lowestCommonAncestor = function(root, p, q) {
    if(root===null){ // 空树的情况
       return null
    }
    if(p.val>q.val){ // 保证p<=q方便后续讨论判断
       return lowestCommonAncestor(root,q,p)
    }
    // 一般情况讨论
    // 1 在跟两侧
    if(root.val>=p.val&&root.val<=q.val){ // 根节点在中心,说明就是最近祖先
        return root
    }
    // 2、都在左侧
    if(root.val>q.val){
        return lowestCommonAncestor(root.left,p,q)
    }
    // 3、都在右侧
    if(root.val<p.val){
        return lowestCommonAncestor(root.right,p,q)
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值