【路飞】二叉树学习

318 阅读3分钟

定义

二叉树是每个节点最多有两个子数的树结构

  var tree = {
     node: 6,
     left: {
         node: 5,
         left: {
             node: 4,
         }
         right: {
             node: 3
         }
     },
     right: {
         node: 2,
         right: {
             node: 1,
         }
     }
  }

二叉树的高度

一般用O(h)来表示更合适,h表示树的层级

二叉树的分类

满二叉树

20200806185805576.png

深度为h,有2^h-1个节点的二叉树

完全二叉树

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h-1个节点。

20200920221638903.png

二叉搜索树

二叉搜索树是一个有序树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

20200806190304693 (1).png

平衡二叉搜索树

平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

20200806190511967.png

用数组表示二叉树

20200920200429452.png 如果父节点的数组下表是i,那么它的左孩子就是i * 2 + 1,右孩子就是 i * 2 + 2。

分治法

将大规模问题拆分为若干小规模的同类型问题去处理的算法思想; 把一个复杂的问题分成两个或者更多的相同或相似的子问题,再把子问题分成更小的子问题......直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

什么样的数据结构适合分治法?

二叉树:整颗树的左子树和右子树都是二叉树,二叉树的大部分题都可以使用分治法解决

二叉树深度遍历的三种方式

非递归方式实现前中后序遍历;

function traversal(root, type) {
  if (!root) return [];
  const order = {
    pre: ["val", "left", "right"],
    in: ["left", "val", "right"],
    post: ["left", "right", "val"],
  };
  const result = [];
  const stack = [root];
  while (stack.length) {
    const item = stack.pop();
    if (typeof item == "number") {
      result.push(item);
    } else {
      const itemArr = order[type];
      for (let i = itemArr.length; i >=0; i--) {
        const key = itemArr[i];
        if (item[key]) {
          stack.push(item[key]);
        }
      }
    }
  }
  return result;
}
  • 前序遍历(递归) 根节点-左子树-右子树
var result = []
var dfs = function(nodes) {
  if(nodes.node) {
    result.push(nodes.node)
    nodes.left && dfs(nodes.left)
    nodes.right && dfs(nodes.right)
  }
}
dfs(nodes)
  • 中序遍历 左子树-根节点-右子树
var result = []
var dfs = function(nodes) {
  if(nodes.node) {
    nodes.left && dfs(nodes.left)
    result.push(nodes.node)
    nodes.right && dfs(nodes.right)
  }
}
dfs(nodes)
  • 后序遍历 左子树-右子树-根节点
var result = []
var dfs = function(nodes) {
  if(nodes.node) {
    nodes.left && dfs(nodes.left)
    nodes.right && dfs(nodes.right)
    result.push(nodes.node)
  }
}
dfs(nodes)

前序、中序、后序指根节点被遍历的顺序;

二叉树广度优先遍历

优点:不会打乱数据结构

递归实现

var result = []
var queue = [nodes]
var bfs3 = function(count) {
  count = count || 0
  if(queue[count]) {
    result.push(queue[count].node)
    var left = queue[count].left
    var right = queue[count].right
    if(left) {
      queue.push(left)
    }
    if(right) {
      queue.push(right)
    }
    bfs3(++count)
  }
}
dfs3()

非递归实现

var bfs4 = function(nodes) {
  var result = []
  var queue = []
  var pointer = 0
  queue.push(nodes)
  while(pointer < queue.length) {
    var item = queue[pointer++] // 这里不使用 shift 方法(复杂度高),用一个指针代替
    result.push(item.node)
    //console.log(item.node)
    item.left && queue.push(item.left)
    item.right && queue.push(item.right)
  }
  return result
}
console.log(bfs4(nodes))

总结:大部分考题都是深度遍历相关的,所以学好深度遍历是关键。

写在最后

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞关注