翻转|镜像二叉树

华为面试题——将二叉树的两个孩子换位置,即左变右,右变左。

90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so f* off.

递归实现

先序遍历每个结点,翻转每个结点。

下面来分析具体的实现思路:

  • 对于根结点为空的情况

    这种情况需要排除,因为null不是一个对象,不可能存在左右子树并且可以翻转的情况

  • 对根不为空的情况,翻转该节点

JavaScript代码实现二叉树翻转

reverseTree (root = this.tree) {if (root &&root.data) {[root.leftChild, root.rightChild] = [root.rightChild, root.leftChild]this.reverseTree(root.leftChild)this.reverseTree(root.rightChild)}
}

循环,栈存储(DFS,非递归)

本质思想是,左右节点进行交换,循环翻转每个节点的左右子节点,将未翻转的子节点存入栈中,循环直到栈里所有节点都循环交换完为止。

reverseTree3 (node) {if (!node) {return 0}let queue = [node]while (queue.length) {let temp = queue.shift();[temp.leftChild, temp.rightChild] = [temp.rightChild, temp.leftChild]temp.leftChild && queue.push(temp.leftChild)temp.rightChild && queue.push(temp.rightChild)}return node
}

层序遍历,翻转每层的结点

JavaScript代码实现

reverseTree2 (node = this.tree) {let buckets = [[node]]let i = 0function getChildNode (root, i) {if (!root || !root.data) {return false}i++if (buckets[i] === undefined) {buckets[i] = []}if (root.leftChild) {buckets[i].push(root.leftChild)getChildNode(root.leftChild, i)}if (root.rightChild) {buckets[i].push(root.rightChild)getChildNode(root.rightChild, i)}}getChildNode(node, i)for (let i = 0; i < buckets.length; i++) {for(let j=0;j<buckets[i].length;j++){if(i>1){let parentIndex = buckets[i-1].length-1-Math.floor(i/2)buckets[i][j]['parent']=buckets[i-1][parentIndex]}buckets[i+1].reverse()let leftChildIndex = i*2let rightChildIndex = i*2+1if(buckets[i+1][leftChildIndex]){buckets[i][j]['leftChild']=buckets[i+1][leftChildIndex]}if(buckets[i+1][rightChildIndex]){buckets[i][j]['rightChild']=buckets[i+1][rightChildIndex]}if(i===buckets.length-1){break}}}return node
}

就是翻转每一层的数据

求二叉树的深度

分析过程

  1. 只有一个根结点时,二叉树深度为1

  2. 只有左子树时,二叉树深度为左子树深度加1

  3. 只有右子树时,二叉树深度为右子树深度加1

  4. 同时存在左右子树时,二叉树深度为左右子树中深度最大者加1

JavaScript求二叉树深度,代码实现

getTreeDepth(node){let leftD =1let rightD =1function getDeep(node){if(!node||!node.data){return 0}if(node.leftChild){leftD++getDeep(node.leftChild)}if(node.rightChild){rightD++getDeep(node.rightChild)}}return leftD>rightD?leftD:rightD
}

求二叉树的宽度

二叉树的宽度是啥?我把它理解为具有最多结点数的层中包含的结点数

分析过程

根据上图,我们如何算出二叉树的宽度呢?其实有个很简单的思路:

  1. 算出第一层的结点数,保存

  2. 算出第二层的结点数,保存一二层中较大的结点数

  3. 重复以上过程

getTreeWidth (node) {let queue = [node]let max = 1let width = queue.lengthwhile (queue.length) {width=queue.lengthwhile (width) {let temp = queue.shift()if (temp.leftChild) {queue.push(temp.leftChild)}if (temp.rightChild) {queue.push(temp.rightChild)}width--}max = queue.length > max ? queue.length : max}return max
}

判断一颗二叉树是否为平衡二叉树

解决思路一:按照前序遍历的路线判断。

  1. 判断以根结点的树是否为二叉平衡树。求出左右子树的高度,判断它们的高度差是否超过了1。

  2. 递归判断根的左子树是否为平衡二叉树

  3. 递归判断根的右子树是否为平衡二叉树

解决思路二:按照后序遍历的路线判断

  • 首先,判断它的左子树是否为平衡二叉树

  • 然后在判断它的右子树是否为平衡二叉树

  • 判断它们是否为平衡二叉树的同时,记录它们的左右子树的深度

  • 最后在判断以这个结点为根的树是否为平衡二叉树

在排序数组中查找元素的第一个和最后一个位置(中等)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8 输出: [3,4] 示例 2:

输入: nums = [5,7,7,8,8,10], target = 6 输出: [-1,-1]

二分查找分析与思路

按照正常的二分查找思路,循环找到匹配目标数字的下标,继续将右指针变小查找到第一个目标数字。用新的循环从找到的第一个目标数字下标从左往右查找直到最后一个目标数字。(具体的查找思路见伪代码)

/*** 二分查找数组中,第一出线目标数据的前后位置, 如果没有返回[-1,-1]* @param arr {Array}* @param target {Number}* @return {number[]}*/
function searchRange (arr, target) {// 声明搜索用的左右指针,初始左指针下标0,右指针下标数组末位nums.length-1;let l = 0, r = arr.length - 1// 获取数组中间下标pivot = (l + r) / 2;let pivot = Math.floor((l + r) / 2)// 声明创建结果数组,初始化赋值-1;let res = [-1, -1]// 循环二分查找,直到左指针大于右指针查找结束while (l <= r) {// 若中间位置数字小于目标数字,说明目标数字在pivot右边,将左指针l右移赋值为pivot+1;if (arr[pivot] < target) {l = pivot + 1// 若中间位置数字大于目标数字,说明目标数字在pivot左边,将右指针r左移赋值为pivot-1;} else if (arr[pivot] > target) {r = pivot - 1// 若中间位置数字等于目标数字:} else {// 将pivot作为第一次出现位置存入数组;res[0] = pivot// 右指针r左移赋值为pivot-1,继续查找相等的数字直到找到第一次出现的位置;r = pivot - 1}// 更新pivot;pivot = (l + r) / 2}// 从第一次出现的位置开始,循环往右查找最后一次出现的位置:// 声明pr指针初始赋值为第一次出现位置下标;let pr = res[0]// 当二分查找已找到目标数字时if (pr !== -1) {// 循环直到数组越界或者数组当前元素不等于目标元素时结束:while (pr <= arr.length - 1 && target === arr[pr]) {// 更新最后一次出现位置下标;pr += 1// 更新最后一次出现位置下标;res[1] = pr}}return res
}
console.log(searchRange([-1,5,5,6,8,9,13,22],-2))

参考文章:

使用JavaScript完成二叉树的一些基本操作 https://segmentfault.com/a/1190000016914803

JavaScript二叉树实现和原理完全讲解 www.srcmini.com/1772.html

LeetCode进阶226-翻转二叉树(华为面试题) https://zhuanlan.zhihu.com/p/76818774

面试BAT 却被二叉树秒杀?20 道题帮你一举拿下二叉树算法题 https://zhuanlan.zhihu.com/p/88361872

转载本站文章《讲透学烂二叉树(六):二叉树的笔试题:翻转|宽度|深度》,
请注明出处:https://www.zhoulujun.cn/html/theory/algorithm/TreeGraph/8289.html

讲透学烂二叉树(六):二叉树的笔试题:翻转|宽度|深度相关推荐

  1. 获得无向图连通子图_讲透学烂二叉树(一):图的概念和定义—各种属性特征浅析...

    树和图的概念 图是一种特殊的数据结构,由点和边构成,它可以用来描述元素之间的网状关系,这个网状没有顺序,也没有层次,就是简单的把各个元素连接起来. 图的概念和基本性质 图(graph):图(graph ...

  2. 二叉树第i层中的所有结点_讲透学烂二叉树(二):图中树的定义amp;各类型树的特征分析...

    日常中我们见到的二叉树应用有,Java集合中的TreeSet和TreeMap,C++ STL中的set.map,以及Linux虚拟内存的管理,以及B-Tree,B+-Tree在文件系统,都是通过红黑树 ...

  3. 讲透学烂二叉树(五):分支平衡—AVL树与红黑树伸展树自平衡

    简叙二叉树 二叉树的最大优点的就是查找效率高,在二叉排序树中查找一个结点的平均时间复杂度是O(log₂N): 在<讲透学烂二叉树(二):树与二叉/搜索/平衡等树的概念与特征>提到 二叉排序 ...

  4. 讲透学烂二叉树(三):二叉树的遍历图解算法步骤及JS代码

    二叉树的遍历是指不重复地访问二叉树中所有结点,主要指非空二叉树,对于空二叉树则结束返回. 二叉树的遍历分为 深度优先遍历 先序遍历:根节点->左子树->右子树(根左右),有的叫:前序遍历 ...

  5. 少儿编程100讲轻松学python(六)-pycharm怎么汉化?

    前言 pycharm汉化的方法: 首先下载pycharm汉化包,并找到[resource_zh.jar]文件: 然后打开pycharm安装目录的lib文件夹,将汉化包放入pycharm安装目录lib文 ...

  6. 少儿编程150讲轻松学Scratch(六)-少儿编程命名规则

    前言 少儿编程命名也是很重要的哦,小朋友们代码里面的命名有没有做好呢?有的宝宝可能要会有疑问,老师老师,我给里面的东西怎么命名又不影响程序功能,为什么起个名字也要在意呢? 无论是代码前期搭建的工作,还 ...

  7. 从零开始学C语言数据结构 : 二叉树的顺序结构

    二叉树的顺序结构 1.树 1.1树的概念 1.2树的表示 1.3二叉树 2.二叉树的顺序结构 1.堆 2.堆的实现 2.1.向下调整 2.2堆的创建--向下调整实现 2.3建堆的时间复杂度 2.4堆的 ...

  8. 趣学数据结构--第六章:树

    趣学数据结构---->第六章:树 二叉树 线索二叉树 树的应用 二叉树的深度 二叉树的叶子数 二叉树的结点数 三元组创建二叉树 遍历序列还原树 哈夫曼树 二叉树 二叉树的创建(询问创建以及补空创 ...

  9. c#二叉树 取叶子节点个数_数据结构第四章:树与二叉树(二叉树的概念、性质、特殊二叉树)...

    第四章:树与二叉树(二叉树的逻辑结构) 1.二叉树 二叉树是树结构的一种,故二叉树也是逻辑结构. 二叉树:二叉树是n(n≥0)个结点的有限集合. · 1)n=0时,二叉树为空; · 2)n>0时 ...

最新文章

  1. VHDL中的左移函数
  2. 请求分页算法 Python实现
  3. java正则表达式性能_译:Java 中的正则表达式性能概述
  4. java set排序_Java Set排序的方法
  5. Sequence(BZOJ-1345)
  6. 帝国cms纯php调用,帝国CMS模板中:使用php调用最新文章的代码(非灵动和万能标签)...
  7. 线性插值改变图像尺寸_【图像分割模型】实例分割模型—DeepMask
  8. 「译文」你必须掌握的 7 种 JavaScript 错误类型
  9. 20200805:Java拓扑排序实现力扣207课程表
  10. Open3d之ubuntu18.04源码编译open3d
  11. 如何才能精通C++?原来这点才最重要!
  12. Java有关数组例题_Java基础——数组例题二维数组
  13. atitit.团队建设总结o6o fix
  14. 鸿蒙系统麒麟970芯片支持,受鸿蒙系统影响,众多华为手机或要说再见,包括麒麟970机型!...
  15. 实时查询(otoci)
  16. c语言数字转成大写,c语言 数字变大写 代码
  17. 网站流量统计分析工具,谷歌seo网站流量统计分析工具推荐
  18. 基于虚拟机的VxWorks实验平台设计与实现(读研时的一篇论文)
  19. 51地图标注接口(EZMarker API)
  20. py6_Python 内置函数/普通自定义函数 及参数和返回值

热门文章

  1. android 录像静音,Android – 静音麦克风录制视频时
  2. 上海科技大学计算机浙江分数线,上海科技大学2020录取分数线 上海科技大学录取分数线各省汇总...
  3. Uber入驻四川乐山峨眉地区
  4. 计算机辐射测试,无线路由器辐射测试方法
  5. elementUI表格无数据设置
  6. C语言为什么不会过时?
  7. TC397 LIN Master 用例
  8. 闲人闲谈PS之一项目库存跨公司业务STO解决方案--SAP闲人的开篇
  9. 英特尔至强融核助力国家海洋局探索超算应用
  10. Signal Processing for Active Control chapter3翻译