js實現二叉樹相關算法-leetcode整理(持續更新)

二叉樹node

在此以前,先來回顧一下二叉搜索樹的建立和基本方法
掌握瞭如下代碼,將能熟練地對二叉樹更進一步的操做面試

var Node=function(key){
    this.key=key;
    this.left=null;
    this.right=null;
}
class BinaryTree{
    constructor(){
        this.root=null;
    }

    // 插入節點(按二叉搜索樹要求)
    insert(key){
        let newNode=new Node(key);
        if(this.root==null){
            this.root=newNode;
        }else{
            this.insertNode(this.root,newNode);
        }
    } 
    insertNode(node,newNode){
        if(newNode.key<node.key){
            if(node.left==null){
                node.left=newNode;
            }else{
                this.insertNode(node.left,newNode);
            }
        }else{
            if(node.right==null){
                node.right=newNode;
            }else{
                this.insertNode(node.right,newNode);
            }
        }
    }


    //前序遍歷
    DLR(callback){
        this.traverseNodesDLR(this.root,callback);
    }
    traverseNodesDLR(node,callback){
        if(node!=null){
            callback(node.key);
            this.traverseNodesDLR(node.left,callback);
            this.traverseNodesDLR(node.right,callback)
        }
    }

    // 中序遍歷
    LDR (callback){
       this.traverseNodesLDR(this.root,callback);
     }

    traverseNodesLDR=function (node,callback){
          if(node){
             this.traverseNodesLDR(node.left,callback);
             callback(node.key);
             this.traverseNodesLDR(node.right,callback);
          }
    }

    // 查找最小值
    min(){
        let current=this.root;
        while(current.left!=null){
            current=current.left;
        }
        return current.key;
    }
    // 查找最大值
    max=function(){
        let current=this.root;
        while(current.right!=null){
            current=current.right;
        }
        return current.key;
    }

    //刪除節點
    removeNode = function(node,key){
        if(node ===null){
            return null
        }
        if(key<node.key){
            node.left=this.removeNode(node.left,key);
            return node;
        }else if(key>node.key){
            node.right = this.removeNode(node.right,key);
            return node;
        }else{
        //刪除節點
        //一、刪除沒有左右子樹的節點
        if(node.left ===null && node.right ===null){
            node =null;
            return node;
        }
        //二、刪除只有右子樹的節點
        if(node.left === null){
           node = node.right;
           return node;
        }
        //三、刪除只有左子樹的節點
        if(node.right === null){
            node = node.left;
            return node;
        }
        //四、刪除左右子樹都有的節點

        //4.1查找右子樹中最小的節點N,
        var minNode = getMinNode(node.right);
        //4.2用N替換須要刪除的節點,
        node.key = minNode.key;
        //4.3刪除右子樹最小的節點
        node.right =this. removeNode(node.right,minNode.key);
        return node
        }
    }

     deleteNode = function(key){
        this.removeNode(this.root,key);
     }

}


var callback=item=>console.log(item);

下面的題目全都來自leetcode,直接leetcode搜名字能夠找到。 屬於比較常見的二叉樹相關算法,難度:簡單/中等
代碼均爲簡單易於理解的版本,效率還不錯算法

二叉樹的前中後序遍歷

首先要掌握二叉樹的遍歷方法 主要是兩種方法,遞歸和迭代數組

遞歸很簡單,面試常考迭代函數

  1. 前序遍歷
// 1.遞歸 
 var preorderTraversal = function(root) {
 let res = []
 let traversal = function(root) {
 if(!root) return
 res.push(root.val)
 traversal(root.left)
 traversal(root.right)
 }
 traversal(root)
 return res
 };
 // 2.迭代    顯式維護一個棧
 var preorderTraversal = (root)=>{
 let res = []
 if(!root) return res
 let stack = [root]
 while(stack.length) {
 let top = stack.pop()
 res.push(top.val)
 if(top.right) stack.push(top.right)  // 右節點先入棧
 if(top.left) stack.push(top.left)
 }
 return res
 }
  1. 中序遍歷
// 中序遍歷  迭代
 var inorderTraversal = (root) => {
 let res = []
 if(!root) return res
 let stack = [root]
 while(stack.length||root) {
 let top = stack[stack.length-1]
 while(root) {
 stack.push(root)
 root = root.left
 }
 let root = stack.pop()
 res.push(root.val)
 if(root.right) stack.push(root.right)
 }
 return res
 }
​
 /// 迭代
 var inorderTraversal = function(root) {
 const res = [];
 const stk = [];
 while (root || stk.length) {
 while (root) {
 stk.push(root);
 root = root.left;
 }
 root = stk.pop();
 res.push(root.val);
 root = root.right;   // 對它中序遍歷
 }
 return res;
 };
  1. 後序遍歷

遞歸的方式很簡單,跟前序中序相似,這裏再也不贅述post

var postorderTraversal = function(root) {
      let res = [],stack = []
        while(root||stack.length) {
            res.unshift(root.val)  // 往前插入
            if(root.left) stack.push(root.left)
            if(root.right) stack.push(root.right)
            root = stack.pop()
        }
        return res
};

700. 二叉搜索樹中的搜索

var searchBST = function(root, val) {  
 if(root==null) return null;  
 if(root.val==val) return root;  
 if(root.val<val){  
 /////////這裏不加return返回了undefined  
 //外層函數沒法收到返回值  
 return searchBST(root.right,val)  
 }else {  
 return searchBST(root.left,val)  
 }   
};

1564 二叉搜索樹轉鏈表

思路 二叉搜索樹的中序遍歷時作指針移動操做ui

var convertBiNode = function(root){  
 if(!root)  return null  
 let p=new TreeNode('head')  //哨兵節點  
 let curr=p  //始終指向最新的節點  
 var LDR=function(node){  
 if(!node) return null  
 LDR(node.left)  
 //指針移動  
 node.left=null  //當前節點的左節點置空,不指向其餘節點  
 curr.right=node  //鏈接  
 curr=curr.right  
 LDR(node.right)   
 }   
 LDR(root)  
 return p.right  
}

二叉搜索樹與雙向鏈表

劍指 Offer 36. 二叉搜索樹與雙向鏈表this

//沒有頭節點的循環鏈表
var treeToDoublyList = function(root) {
   if(!root) return
    let head=null
    p=head      //p用於遍歷
    LDR(root)
    p.right=head  //最後首尾的處理
    head.left=p
    return head

    function LDR(node){    //放到外部傳參的話不能改變p
        if(!node) return 
        LDR(node.left)
        {
            if(p){
                p.right=node
            }else{  //最左邊節點
                head=node
            }
            node.left=p
            p=node
        }
        LDR(node.right)
    }
};

平衡二叉樹

var isBalanced = function(root) {
    if(judge(root)==-1) return false
    return true
};
function judge(root){
    if(!root){
        return 0
    }
    let left=judge(root.left)
    if(left==-1)  //該節點左孩子的左右子樹不平衡,直接將該節點也返回不平衡
        return -1
    let right=judge(root.right)
    if(right==-1) return -1
    if (Math.abs(left-right)>1) return -1
    return Math.max(left,right)+1
}

102 二叉樹層序遍歷

關鍵:出隊時左右孩子入隊指針

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
  var levelOrder=function(root){
    let result=[];
    if(root==null)
      return result;
    let queue=[root];  //根節點入隊
    while(queue.length>0){    
      let arr=[];  
      //////根據題目要求,每層的放在一個數組。所以還需一個循環控制 層
      let len=queue.length;
      while(len){      //遍歷當前層
        let node=queue.shift();  //刪除並返回首元素
        arr.push(node.val);
        if(node.left)  queue.push(node.left)
        if(node.right)  queue.push(node.right)
        len--;   ///**易漏
      }
      result.push(arr);
    }
    return result;    //每層放在一個數組中
  }

515\. 在每一個樹行中找最大值

層序遍歷時多了一步code

var largestValues = function(root) {
    if(!root) return []
    let res=[]
    let queue=[root]
    while(queue.length){
        let len=queue.length
        let tmp=[]
        while(len){
            let t=queue.shift()
            if(t.left) queue.push(t.left)
            if(t.right) queue.push(t.right)
            tmp.push(t.val)
            len--
        }
        res.push(Math.max(...tmp))
    }
    return res
};

1603 二叉樹的鏡像

///////////遞歸
var mirrorTree = function(root) {
    let change=function(node){
        if(!node){
            return
        }
        //這裏加入對子節點的判斷能提升效率
        //避免對子節點的兩個null交換
        if(node.left||node.right){
            let temp=node.left       //用[ ]解構交換更快
            node.left=node.right
            node.right=temp
            change(node.left)
            change(node.right) 
        }   
    }
    change(root)
    return root
};

對稱二叉樹

給定一個二叉樹,檢查它是不是鏡像對稱的。

//方法1. 遞歸
var isSymmetric = function(root) {
    if(!root) return true
    let isEqual=function(left,right){
        if(!left&&!right) return true  //到底了,確定爲ture,不然中途會返回false
        if(!left||!right) return false  //一空一不空
        if(left.val!=right.val) return false
        //只剩值相等的狀況,繼續判斷
        return isEqual(left.left,right.right)&&isEqual(left.right,right.left)
    }
    return isEqual(root.left,root.right)
};


//方法2. 層序遍歷
var isSymmetric = (root) => {
  if (!root) return true
  let queue = [root.left, root.right]
  while (queue.length) {          // 隊列爲空表明沒有可入列的節點,遍歷結束
    let len = queue.length        // 獲取當前層的節點數
    for (let i = 0; i < len; i += 2) { // 一次循環出列兩個,因此每次+2***
      let left = queue.shift()    // 左右子樹分別出列
      let right = queue.shift()   // 分別賦給left和right變量
      if ((left && !right) || (!left && right)) return false // 不知足對稱
      if (left && right) { // 左右子樹都存在
        if (left.val !== right.val) return false // 左右子樹的根節點值不一樣
        queue.push(left.left, right.right) // 讓左子樹的left和右子樹的right入列
        queue.push(left.right, right.left) // 讓左子樹的right和右子樹的left入列
      }
    }
  }
  return true // 循環結束也沒有遇到返回false
}

104\. 二叉樹的最大深度

var maxDepth = function(root) {
    if(!root) return 0
    let right=maxDepth(root.right)
    let left=maxDepth(root.left)
    return Math.max(left,right)+1
};

N叉樹的最大深度

深度優先和廣度優勢

  1. 深度優先 遞歸求出每一個子樹的最大深度,再加上根節點
var maxDepth = function(root) {  
 if(!root) return 0;  
 if(!root.children) return 1;  
 let max = 0;  
 for(let i=0; i<root.children.length; i++) {  
 let childDepth = maxDepth(root.children\[i\]);  
 max = Math.max(max, childDepth)  
 }  
 return max + 1;  
};

2.廣度優先 算出總共有幾層便可

// 層序遍歷,迭代  
var maxDepth = function(root) {  
 if(!root) return 0;  
 let queue = \[\];  
 let level = 0;  
 queue.push(root);  
​  
while(queue.length) {  
 let length = queue.length;  
 while(length -- ) {  
 let node = queue.shift();  
 node.children && (queue = queue.concat(node.children));  
 }  
 level ++;  
}  
return level;  
};

111\. 二叉樹的最小深度

var minDepth = function(root) {
    if(!root) return 0;
    let left=minDepth(root.left);
    let right=minDepth(root.right);
    if(!root.left||!root.right) return left+right+1;
    return Math.min(left,right)+1;  //左右均不爲空
};

面試題34. 二叉樹中和爲某一值的路徑

var pathSum = function(root, sum) {
    let res = []
    let dfs=function(root, path, sum){
        if(!root) return
        sum -= root.val  
        if(sum == 0 && !root.left && !root.right){
            res.push([...path, root.val])
            return
        }
    path.push(root.val)
    dfs(root.left, path, sum)
    dfs(root.right, path, sum)
    path.pop()
}
    dfs(root, [], sum)
    return res
};

兩顆二叉樹合併

都有節點時求和

var mergeTrees = function(t1, t2) {
    if(!t2) return t1  //t2空。t1是否空不重要
    if(!t1) return t2  //t1空,t2不空
    if(t1&&t2){
        t1.val+=t2.val
    }
   t1.left=mergeTrees(t1.left,t2.left)
   t1.right=mergeTrees(t1.right,t2.right)
    return t1   //新樹用t1返回
};

數組轉二叉排序樹(平衡二叉樹)

var sortedArrayToBST = function(nums){
    if(nums.length==0){
        return null
    }
    let m=Math.floor((nums.length-1)/2)
    let p=new TreeNode(nums[m])
    p.left=sortedArrayToBST(nums.slice(0,m))
    p.right=sortedArrayToBST(nums.slice(m+1))
    return p
};

二叉樹公共祖先

(注意是二叉搜索樹,容易肯定在哪一個子樹上)

  1. 從根節點開始遍歷樹
  2. 若是節點p和節點q都在右子樹上,那麼以右孩子爲根節點繼續 1 的操做
  3. 若是節點p 和節點q都在左子樹上,那麼以左孩子爲根節點繼續 1 的操做
  4. 若是條件 2 和條件 3 都不成立,已經找到
  • 臨界條件:

    • 根節點是空節點
    • 根節點是q節點
    • 根節點是p節點
  • 從左右子樹分別進行遞歸,即查找左右子樹上是否有p節點或者q節點

    • 左右子樹均無p節點或q節點
    • 左子樹找到,右子樹沒有找到,返回左子樹的查找結果
    • 右子樹找到,左子樹沒有找到,返回右子樹的查找結果
    • 左、右子樹均能找到,說明此時的p節點和q節點在當前root節點兩側,返回root節點
var lowestCommonAncestor = function(root, p, q) {
    if(!root||root===p||root===q)
        return root;
    let left = lowestCommonAncestor(root.left,p,q);
    let right = lowestCommonAncestor(root.right,p,q);
    if(left&& right)  //分別在root的左右兩邊
        return root;
    return left!= null ? left : right;  //都在左子樹或都在右子樹上
};

劍指 Offer 33. 二叉搜索樹的後序遍歷序列

//分治  每次判斷左子樹全部節點均小於當前根節點,右子樹均大於當前根節點
//
var verifyPostorder = function (postorder){
    let len=postorder.length
    if(len<3) return true   //長度0,1,2時,必定能構造出來知足條件的樹
    root=postorder[len-1]

    //劃分,找第一個比根節點大的
    let i=0;
    for(;i<len-1;i++){
        if(postorder[i]>root) break
    }
    //右子樹是否知足
    let flag=true
    for(let j=i+1;j<len;j++){
        if(postorder[j]<root) flag = false
    }
    ////注意這裏的區間
    if(flag) return verifyPostorder(postorder.slice(0,i))&&verifyPostorder(postorder.slice(i,len-1))
    (else) return false
}

劍指 Offer 07. 重建二叉樹

var buildTree = function(preorder, inorder) {
    if(!preorder.length) return null  //上個節點爲葉節點,左右爲null
    let node=new TreeNode(preorder[0]),i=0
    for(;i<inorder.length;i++){
        if(inorder[i]===node.val) break
    }
    node.left=buildTree(preorder.slice(1,1+i),inorder.slice(0,i))
    node.right=buildTree(preorder.slice(1+i),inorder.slice(i+1))
    return node
};

劍指 Offer 26. 樹的子結構

題意:判斷A是否是B的子樹

var isSubStructure = function(A, B) {
    if(!A||!B) return false
    if(A.val===B.val&& find(A.left,B.left)&&find(A.right,B.right)) return true  //根節點相同而且左右子樹都相同(find遞歸)
    else{
        return isSubStructure(A.left,B)||isSubStructure(A.right,B)
    }
};
let find=function(A,B){
     if(!B) return true
    if(!A) return false
    if(A.val!==B.val){
        return false
    }
    return find(A.left,B.left)&&find(A.right,B.right)
}
相關文章
相關標籤/搜索