fucking-algorithm icon indicating copy to clipboard operation
fucking-algorithm copied to clipboard

东哥带你刷二叉树(纲领篇) :: labuladong的算法小抄

Open utterances-bot opened this issue 3 years ago • 59 comments

文章链接点这里:东哥带你刷二叉树(纲领篇)

评论礼仪 见这里,违者直接拉黑。

utterances-bot avatar Jan 10 '22 02:01 utterances-bot

牛蛙牛蛙占个楼

Deserant avatar Jan 10 '22 02:01 Deserant

学到不少 NICE

deepbreath373 avatar Jan 14 '22 09:01 deepbreath373

你只需要思考每一个节点应该做什么,其他的不用你管,抛给二叉树遍历框架,递归会对所有节点做相同的操作。

遇到一道二叉树的题目时的通用思考过程是:是否可以通过遍历一遍二叉树得到答案?如果不能的话,是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案? 如果需要设计到子树信息, 建议使用后续遍历.

Asber777 avatar Jan 17 '22 05:01 Asber777

@Asber777 这个总结很好

labuladong avatar Jan 17 '22 06:01 labuladong

666

a572251465 avatar Jan 19 '22 08:01 a572251465

感觉搜索二叉树深度第一个解法,不能用全局变量,要通过指针来传递res,depth

xiazhi1 avatar Jan 21 '22 02:01 xiazhi1

刷了一些算法吧,刷二叉树类问题确实是承前启后。遍历解法,深一步就是回溯思想。分解子问题解法,深一步就是动态规划。

1181406961 avatar Jan 27 '22 04:01 1181406961

js 版二叉树直径:

var diameterOfBinaryTree = function(root) {
  let maxDiameter = 0;
  // 对每个节点计算直径,求最大直径
  maxDepth(root);
  return maxDiameter;

  // 计算二叉树的最大深度
  function maxDepth(root) {
    if (root == null) {
      return 0;
    }
    const leftMax = maxDepth(root.left);
    const rightMax = maxDepth(root.right);
    // 后序位置顺便计算最大直径
    const myDiameter = leftMax + rightMax;
    maxDiameter = Math.max(maxDiameter, myDiameter);

    return Math.max(leftMax, rightMax) + 1;
  }
};

jswxwxf avatar Feb 06 '22 09:02 jswxwxf

二叉树的简单 js 实现,可以用来调试代码:

class TreeNode {
  constructor(value) {
    this.val = value;
    this.left = null;
    this.right = null;
  }

  insert(values) {
    const queue = [this];
    let i = 0;
    while (queue.length > 0) {
      let current = queue.shift();
      for (let side of ["left", "right"]) {
        if (!current[side]) {
          if (values[i] !== null) {
            current[side] = new TreeNode(values[i]);
          }
          i++;
          if (i >= values.length) return this;
        }
        if (current[side]) queue.push(current[side]);
      }
    }
    return this;
  }
}

const tree = new TreeNode(15);
tree.insert([1, 2, 3, 4, 3, 8, 7, 5, 10, 12, 11, null, null, null]);

jswxwxf avatar Feb 08 '22 07:02 jswxwxf

js 二叉树分治前序遍历

function preorderTraverse(root) {
  const res = [];
  if (root == null) {
    return res;
  }
  // 前序遍历的结果,root.val 在第一个
  res.push(root.val);
  // 利用函数定义,后面接着左子树的前序遍历结果
  res.push(...preorderTraverse(root.left));
  // 利用函数定义,最后接着右子树的前序遍历结果
  res.push(...preorderTraverse(root.right));
  return res;
}

jswxwxf avatar Feb 08 '22 07:02 jswxwxf

牛逼

StormWangxhu avatar Feb 10 '22 12:02 StormWangxhu

坚持学习!

uncle-leeeeee avatar Feb 11 '22 08:02 uncle-leeeeee

515 的题其实利用层序遍历有一种更通用的模板:

var largestValues = function(root) {
    const level = []
    const traverse = (root, depth) => {
        if(!root) return
        if(!level[depth]) level[depth] = []
        traverse(root.left, depth+1)
        level[depth].push(root.val)
        traverse(root.right, depth+1)
    }
    traverse(root, 0)
    const ans = level.map(arr => Math.max.apply(null, arr))
    return ans
};

polaris-dxz avatar Feb 15 '22 10:02 polaris-dxz

求二叉树深度那里还是不太理解为什么在离开结点之前depth要自减一次

cold-runner avatar Feb 18 '22 16:02 cold-runner

啊!突然懂了,左孩子已经执行了一次depth++,在离开结点之前,右孩子也进行了depth++,该层的depth加了两次,所以离开前要让depth自减一次以维护。

cold-runner avatar Feb 18 '22 16:02 cold-runner

@cold-runner 这样理解好像不对,是因为当前节点存在,所以进入了当前节点深度++;退出节点时,因为是深度遍历,所以深度要--才能将深度维护正确。

Hans-Kl avatar Feb 21 '22 09:02 Hans-Kl

打印每一个节点所在的层数, right的递归应该是减一吧 ..... 怀疑半天,画图推了一下才确定

Petrichor-sky avatar Feb 23 '22 17:02 Petrichor-sky

@cold-runner @Hans-Kl 看看回溯算法框架就明白了

labuladong avatar Feb 25 '22 06:02 labuladong

@labuladong,求最大深度的第一种解法中

// 记录最大深度
int res = 0;
// 记录遍历到的节点的深度
int depth = 0;

// 主函数
int maxDepth(TreeNode root) {
	traverse(root);
	return res;
}

// 二叉树遍历框架
void traverse(TreeNode root) {
	if (root == null) {
		// 到达叶子节点,更新最大深度
		res = Math.max(res, depth);
		return;
	}
	// 前序位置
	depth++;
	traverse(root.left);
	traverse(root.right);
	// 后序位置
	depth--;
}

res = Math.max(res, depth);这行代码,可以放到前序位置那里,即depth++后面。也可以得到答案,但这两种方式有什么大的区别吗?比如时间效率问题上。我用leetcode运行测试这两种写法执行时间都是显示0s通过。

Jackwong0716 avatar Mar 03 '22 07:03 Jackwong0716

@Jackwong0716 本质上没什么区别,更新答案的时机不同罢了

labuladong avatar Mar 04 '22 02:03 labuladong

// 定义:输入一棵二叉树的根节点,返回这棵树的前序遍历结果 List<Integer> preorderTraverse(TreeNode root) { List<Integer> res = new LinkedList<>(); if (root == null) { return res; } // 前序遍历的结果,root.val 在第一个 res.add(root.val); // 利用函数定义,后面接着左子树的前序遍历结果 res.addAll(preorderTraverse(root.left)); // 利用函数定义,最后接着右子树的前序遍历结果 res.addAll(preorderTraverse(root.right)); } 这个函数忘记写返回值了!!! 应该在末尾加上 return res;

kratosky avatar Mar 04 '22 10:03 kratosky

@kratosky 感谢指出,已修正

labuladong avatar Mar 05 '22 04:03 labuladong

请问,力扣第 543 题「 二叉树的直径」中, 为什么是int myDiameter = leftMax + rightMax,而不是int myDiameter = leftMax + rightMax + 2?我理解这个直径还需要加上root到左子树的1和root到右子树的1。

FZ20192019 avatar Mar 24 '22 03:03 FZ20192019

@FZ20192019 你自己画个图理解下就知道了,maxDepth 算出来的其实就是树枝的条数,已经包含了你说的这两条树枝。

labuladong avatar Mar 24 '22 04:03 labuladong

2022.3.30 mark

billgoo avatar Mar 30 '22 06:03 billgoo

nice

MaeserYang avatar Apr 13 '22 07:04 MaeserYang

nice

cheng-miao avatar Apr 16 '22 11:04 cheng-miao

打卡

Sm1lence avatar Apr 19 '22 06:04 Sm1lence

真不错,这篇文章是属于看了一圈东哥文章之后必须回头来看的文,真的配得上纲领这个词。刷了一个月回过头看这篇文章特别有感觉。

9Tachikoma avatar Apr 20 '22 08:04 9Tachikoma

厉害,东哥

Andy0ma avatar Apr 23 '22 08:04 Andy0ma