笔记 前端需要了解的算法题--树
2020-06-06 05:40
120 查看
二叉树的先序,中序,后序遍历
先序遍历表示先访问根节点,然后访问左节点,最后访问右节点。
中序遍历表示先访问左节点,然后访问根节点,最后访问右节点。
后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。
递归实现
function TreeNode(val) { this.val = val; this.left = this.right = null; } var traversal = function(root) { if (root) { // 先序 console.log(root); traversal(root.left); // 中序 // console.log(root); traversal(root.right); // 后序 // console.log(root); } };
非递归实现
非递归实现使用了栈的结构,通过栈的先进后出模拟递归实现。
先序遍历
function pre(root) { if (root) { let stack = []; // 先将根节点 push stack.push(root); // 判断栈中是否为空 while (stack.length > 0) { // 弹出栈顶元素 root = stack.pop(); console.log(root); // 因为先序遍历是先左后右,栈是先进后出结构 // 所以先 push 右边再 push 左边 if (root.right) { stack.push(root.right); } if (root.left) { stack.push(root.left); } } } }
中序遍历
function mid(root) { if (root) { let stack = []; // 中序遍历是先左再根最后右 // 所以首先应该先把最左边节点遍历到底依次 push 进栈 // 当左边没有节点时,就打印栈顶元素,然后寻找右节点 // 对于最左边的叶节点来说,可以把它看成是两个 null 节点的父节点 // 左边打印不出东西就把父节点拿出来打印,然后再看右节点 while (stack.length > 0 || root) { if (root) { stack.push(root); root = root.left; } else { root = stack.pop(); console.log(root); root = root.right; } } } }
后序遍历
function pos(root) { if (root) { let stack1 = []; let stack2 = []; // 后序遍历是先左再右最后根 // 所以对于一个栈来说,应该先 push 根节点 // 然后 push 右节点,最后 push 左节点 stack1.push(root); while (stack1.length > 0) { root = stack1.pop(); stack2.push(root); if (root.left) { stack1.push(root.left); } if (root.right) { stack1.push(root.right); } } while (stack2.length > 0) { console.log(s2.pop()); } } }
中序遍历的前驱后继节点
实现这个算法的前提是节点有一个 parent 的指针指向父节点,根节点指向 null 。
如图所示,该树的中序遍历结果是
4, 2, 5, 1, 6, 3, 7
前驱节点
对于节点 2 来说,他的前驱节点就是 4 ,按照中序遍历原则,可以得出以下结论
- 如果选取的节点的左节点不为空,就找该左节点最右的节点。对于节点 1 来说,他有左节点 2 ,那么节点 2 的最右节点就是 5
- 如果左节点为空,且目标节点是父节点的右节点,那么前驱节点为父节点。对于节点 5 来说,没有左节点,且是节点 2 的右节点,所以节点 2 是前驱节点
- 如果左节点为空,且目标节点是父节点的左节点,向上寻找到第一个是父节点的右节点的节点。对于节点 6 来说,没有左节点,且是节点 3 的左节点,所以向上寻找到节点 1 ,发现节点 3 是节点 1 的右节点,所以节点 1 是节点 6 的前驱节点
function predecessor(node) { if (!node) return // 结论 1 if (node.left) { return getRight(node.left) } else { let parent = node.parent // 结论 2 3 的判断 while(parent && parent.right === node) { node = parent parent = node.parent } return parent } } function getRight(node) { if (!node) return node = node.right while(node) node = node.right return node }
后继节点
对于节点 2 来说,他的后继节点就是 5 ,按照中序遍历原则,可以得出以下结论
- 如果有右节点,就找到该右节点的最左节点。对于节点 1 来说,他有右节点 3 ,那么节点 3 的最左节点就是 6
- 如果没有右节点,就向上遍历直到找到一个节点是父节点的左节点。对于节点 5 来说,没有右节点,就向上寻找到节点 2 ,该节点是父节点 1 的左节点,所以节点 1 是后继节点
function successor(node) { if (!node) return // 结论 1 if (node.right) { return getLeft(node.right) } else { // 结论 2 let parent = node.parent // 判断 parent 为空 while(parent && parent.left === node) { node = parent parent = node.parent } return parent } } function getLeft(node) { if (!node) return node = node.left while(node) node = node.left return node }
需要求出一颗二叉树的最大深度
var maxDepth = function(root) { if (!root) return 0 return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1 };
相关文章推荐
- 笔记 前端需要了解的算法题--动态规划
- 笔记 前端需要了解的JavaScript 与 HTML 之间交互的事件模型分为几个阶段
- 笔记 前端需要了解的微信小程序与H5的区别
- 笔记 前端需要了解的axios
- 笔记 前端需要了解的html5 为什么只需要写DOCTYPE
- 笔记 前端需要了解的MVVM
- 笔记 前端需要了解的优化图像的方法
- 笔记 前端需要了解的渐进增强和优雅降级之间的不同
- 笔记 前端需要了解的ajax
- 笔记 前端需要了解的微信小程序相关文件类型
- 笔记 前端需要了解的小程序生命周期
- 笔记 前端需要了解的时间复杂度
- 笔记 前端需要了解的jQuery选择器
- 笔记 前端需要了解的浏览器兼容问题
- 笔记 前端需要了解的CDN
- 笔记 前端需要了解的html5新元素
- 笔记 前端需要了解的模块化知识
- 笔记 前端需要了解的浏览器向服务器的请求方式
- 笔记 前端需要了解的html5语义化
- 笔记 前端需要了解的线程与进程的区别