JS中的树(含leetcode例题)<持续更新~>

文章目录

与其他数据结构进行对比
  • 数组

    • 优点:下标值访问效率高;
    • 缺点:查询、插入、删除效率低;
      • 查询一般使用先排序、后二分;
      • 插入和删除在数组前面的位置时,会产生大量位移操作,效率低
  • 链表

    • 优点:插入和删除效率高;
    • 缺点:查询效率低,需要从头开始访问
  • 哈希表

    • 优点:插入、查询、删除效率高
    • 缺点:空间利用率不高;哈希表中的元素是无序的
  • 树:树结构综合了上面的部分优点,每种树结构都有自己独特的应用场景。

常见表示方法
普通表示法

请添加图片描述

儿子-兄弟表示法

只记录左儿子和右兄弟

请添加图片描述

儿子-兄弟表示法旋转形成二叉树

将左儿子和右兄弟作为父节点的二叉,所以二叉树能够模拟所有的树

请添加图片描述

二叉树
满二叉树

除了最下一层叶子节点外,每层节点都有两个子节点

请添加图片描述

完全二叉树

除了最下一层叶子节点外,每层节点都有两个子节点

最下一层叶子节点从左向右连续存在,只缺右侧若干节点

满二叉树是特殊的完全二叉树

请添加图片描述

二叉树的表示

用数组表示非完全二叉树时,空着的节点也要占位,会造成空间浪费,所以一般我们采用链表的方式

二叉搜索树(BST)

满足条件:

  1. 非空左子树的所有键值小于其根节点的键值
  2. 非空右子树的所有键值大于其根节点的键值
  3. 左、右子树本身也是二叉搜索树

请添加图片描述

当我们进行搜索的时候,如果节点大于搜索值,就向左边继续查找;节点小于搜索值,就向右边继续查找

二叉搜索树的封装

基本封装以及插入操作

// 封装二叉搜索树
// 此二叉搜索树只包含key,如果需要value的话在Node构造函数中添加value即可
function BinarySearchTree() {
  // 每一个节点包含key、left、right
  function Node(key){
    this.key = key
    this.left = null
    this.right = null
  }

  // 属性
  this.root = null

  // 方法
  // 实现插入
  BinarySearchTree.prototype.insert = function(key){
    // 1.根据key创建节点
    var newNode = new Node(key)
    // 2.判断根节点是否有值
    if(this.root == null){
      this.root = newNode
    } else{
      // 如果节点有值,就调用insertNode比较
      this.insertNode(this.root,newNode)
    }
  }
  // 辅助插入方法,用于节点比较
  // 每次比较都将从根节点开始
  BinarySearchTree.prototype.insertNode = function (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)
      }  
    }
  }
}
遍历树
  • 深度优先

    • 先序遍历:根–>左子树–>右子树
      请添加图片描述
      // 2.实现遍历
      // (1)先序遍历
      BinarySearchTree.prototype.preOrderTraversal = function(handler){
        this.preOrderTraversalNode(this.root,handler)
      }
      // 辅助遍历方法,用于节点遍历
      BinarySearchTree.prototype.preOrderTraversalNode = function(node,handler){
        if(node != null){
          // 打印当前节点
          handler(node.key)
          // 遍历所有左子树
          this.preOrderTraversalNode(node.left,handler)
          // 左子树遍历完后再遍历右子树
          this.preOrderTraversalNode(node.right,handler)
        }
      }
    // handler是一个回调函数,参数为每一次遍历的key
    
    • 中序遍历:左子树–>根–>右子树
      请添加图片描述
      // 调换一下位置即可
      BinarySearchTree.prototype.midOrderTraversalNode = function(node,handler){
        if(node != null){
          this.preOrderTraversalNode(node.left,handler)
          handler(node.key)
          this.preOrderTraversalNode(node.right,handler)
        }
      }
    
    • 后序遍历:左子树–>右子树–>根
      请添加图片描述
      // 调换一下位置即可
      BinarySearchTree.prototype.postOrderTraversalNode = function(node,handler){
        if(node != null){
          this.preOrderTraversalNode(node.left,handler)
          this.preOrderTraversalNode(node.right,handler)
          handler(node.key)
        }
      }
    
  • 广度优先(使用队列完成)

基本思路就是:谁出队列,就把它的左右子节点入队列

  // (2) 层序遍历(广度遍历)
  BinarySearchTree.prototype.levelOrder = function(handler){
    this.levelOrderNode(this.root,handler)
  }
  // 层序遍历辅助方法
  BinarySearchTree.prototype.levelOrderNode = function(node,handler){
    let queue = []
    if(node != null){
      queue.push(node)
      while(queue.length>0){
        let nowNode = queue.shift()
        handler(nowNode.key)
        if(nowNode.left) queue.push(nowNode.left)
        if(nowNode.right) queue.push(nowNode.right)
      }
    }
  }
搜索特定值
  //3. 实现搜索
  BinarySearchTree.prototype.search = function(key){
    // 获取根节点
    var node = this.root
    // 循环搜索
    while(node != null){
      if(key<node.key){
        node = node.left
      }else if(key>node.key){
        node = node.right
      }else{
        return true
      }
    }
    return false
  }
leetcode例题
144. 二叉树的前序遍历
var preorderTraversal = function(root) {
    let res = []
    preorderTraversalNode(root,res)
    return res
};

var preorderTraversalNode = function (root,res){
    if(root != null){
        res.push(root.val)
        preorderTraversalNode(root.left,res)
        preorderTraversalNode(root.right,res)
    }
}
94. 二叉树的中序遍历
var inorderTraversal = function(root) {
    let res = []
    inorderTraversalNode(root,res)
    return res
};

var inorderTraversalNode = function (root,res){
    if(root != null){
        inorderTraversalNode(root.left,res)
        res.push(root.val)
        inorderTraversalNode(root.right,res)
    }
}
145. 二叉树的后序遍历
var postorderTraversal = function(root) {
    let res = []
    postorderTraversalNode(root,res)
    return res
};

var postorderTraversalNode = function (root,res){
    if(root != null){
        postorderTraversalNode(root.left,res)
        postorderTraversalNode(root.right,res)
        res.push(root.val)
    }
}
102. 二叉树的层序遍历
var levelOrder = function(root) {
    //二叉树的层序遍历
    let res=[],queue=[];
    queue.push(root);
    if(root===null){
        return res;
    }
    while(queue.length!==0){
        // 记录当前层级节点数
        let length=queue.length;
        //存放每一层的节点 
        let curLevel=[];
        for(let i=0;i<length;i++){
            let node=queue.shift();
            curLevel.push(node.val);
            // 存放当前层下一层的节点
            if(node.left) queue.push(node.left);
            if(node.right) queue.push(node.right);
        }
        //把每一层的结果放到结果数组
        res.push(curLevel);
    }
    return res;
};
104. 二叉树的最大深度
var maxDepth = function(root) {
    if(!root){
        return 0
    }else{
        // 递归计算每层的左子节点和右子节点谁最深
        const left = maxDepth(root.left)
        const right = maxDepth(root.right)
        return Math.max(left,right) + 1
    }
};
101. 对称二叉树
const isSymmetric = (root) => {
    const check = (left, right) => {
        if (left == null && right == null) { // 左右为null,对称
           return true;
        }
        if (left && right) { // 左右相等,一个的左子节点等于另一个的右子节点,之后同理
            return left.val == right.val && check(left.left, right.right) && check(left.right, right.left);
        }
        return false;        // 一个子树存在一个不存在,不对称
    };       
    return check(root.left, root.right); // 继续递归判断
};
700. 二叉搜索树中的搜索
// 要善于使用三元表达式简化if语句
var searchBST = function(root, val) {
    while(root){
        if(val == root.val){
            return root
        }
        root = val < root.val ? root.left : root.right
    }
    return null
};
701. 二叉搜索树中的插入操作
var insertIntoBST = function(root, val) {
    // 遇到节点为空的时候,即为可插入位置,创建一个新的节点
    if(root == null){
        return new TreeNode(val)
    }
    if(root.val < val){
        root.right = insertIntoBST(root.right,val)
    }else{
        root.left = insertIntoBST(root.left,val)
    }
    return root
};
112. 路径总和

使用递归,将这个问题划分成多个小问题

即,targetSum减去当前节点的值就是要求的剩余节点的值

我们递归接下来的左子树和右子树

边界为,当前节点的下一个节点为空

// 递归
var hasPathSum = function(root, targetSum) {
    // 遍历到空节点,没有满足返回false
    if(root == null){
        return false
    }
    // 遍历到叶子节点
    if(root.left == null && root.right == null){
        return targetSum-root.val == 0
    }
    // 其他情况下,递归
    return hasPathSum(root.left, targetSum-root.val) || hasPathSum(root.right,targetSum-root.val)
};
226. 翻转二叉树

递归保存当前节点的左右节点,当前节点的左节点等于保存的右节点;同理当前节点的右边节点等于保存的左节点

var invertTree = function(root) {
    if(root == null){
        return root
    }else{
        const left = invertTree(root.left)
        const right = invertTree(root.right)
        root.left = right
        root.right = left
    }
    return root
};
剑指 Offer 32 - III. 从上到下打印二叉树 III

本题主要还是遍历二叉树,只是在中间多了一步判断是否需要翻转

var levelOrder = function(root) {
    let res = [] ,queue = []
    if(root == null){
        return res
    }
    queue.push(root)
    let flag = false
    while(queue.length != 0){
        let cur = []
        let length = queue.length
        for(let i=0;i<length;i++){
            let node = queue.shift()
            cur.push(node.val)
            if(node.left) queue.push(node.left)
            if(node.right) queue.push(node.right)
        }
        flag = !flag
        if(flag){
            res.push(cur)
        }else{
            res.push(cur.reverse())
        }
    }
    return res
};
98. 验证二叉搜索树
// 递归 
 // max是为了帮助node.left判断
 // min是为了帮助node.right判断
let fun = function(node,min,max){
    if(node == null){
        return true
    }
    if(node.val <= min || node.val>=max){
        return false
    }
    return fun(node.left,min,node.val) && fun(node.right,node.val,max)
}
var isValidBST = function(root) {
    return fun(root,-Infinity,Infinity)
};
653. 两数之和 IV - 输入 BST
 // 一直递归向map中set
 // 直到找到返回true
 // 或者递归完所有都没找到返回false
var findTarget = function(root, k) {
    let map = new Map()
    let fun = function(root,k){
        if(!root){
            return false
        }
        if(map.has(k-root.val)){
            return true
        }
        map.set(root.val)
        return fun(root.left,k) || fun(root.right,k)
    }
    return fun(root,k)
};
235. 二叉搜索树的最近公共祖先
 // 如果p,q都小于根节节点,则继续递归根节点的左边进行比较
 // 如果p,q都大于根节节点,则继续递归根节点的右边进行比较
 // 最终返回该节点
var lowestCommonAncestor = function(root, p, q) {
    if(p.val < root.val && q.val < root.val){
        return lowestCommonAncestor(root.left,p,q)
    }
    if(p.val > root.val && q.val> root.val){
        return lowestCommonAncestor(root.right,p,q)
    }
    return root
};
你可以使用以下方法对二维向量vector<vector<int>>进行排序: 1. 使用STL的sort函数进行排序。引用提到了一些排序算法,比如堆排序(heap_sort),希尔排序(shell_sort),选择排序(select_sort)和快速排序(quick_sort_swap)。你可以根据需要选择其一个算法对vector<vector<int>>进行排序。 2. 如果你想在创建二维向量时就进行排序,你可以使用引用的方法通过vector创建二维数组,并在创建时对其的元素进行排序。 3. 如果你已经创建了一个二维向量,你可以使用引用的方法使用resize函数对二维向量进行重新分配,并在重新分配时对其的元素进行排序。 无论你选择哪种方法,都可以对二维向量vector<vector<int>>进行排序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [vectorstring和vectorint](https://blog.csdn.net/toCVer/article/details/122484732)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [leetcode2sumc-Code-Storm2019:力密码2019](https://download.csdn.net/download/weixin_38700430/19937142)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的战斗鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值