当前位置: 首页 > article >正文

使用javascript+canvas显示二叉树

1  创建二叉树节点类

首先,需要一个TreeNode类表示二叉树节点:

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

2. 生成二叉树

lass FullBinaryTree {
  constructor(level) {
    const _Root = this.getFullBinaryTree(level);
    this.getRoot = () => {
      return _Root;
    };
  }
  getFullBinaryTree(level) {
    let index = 0;
    function fullTree(n) {
      if (n === 0) return null;
      let root = new TreeNode(index++);
      root.left = fullTree((n - 1) / 2);
      root.right = fullTree((n - 1) / 2);
      return root;
    }
    // 2的level次方 -1 = 总节点数
    return fullTree(Math.pow(2, level) - 1);
  }
}

3.  计算二叉树深度

const getTreeDepth = (root) => {
  				let depth = 0; // 当前深度
  				let max = 0; // 当前最大深度
  				const solve = (node) => {
    			if (!node) {
      				if (depth >= max) {
        					max = depth;
     				}
      				return 0;
    			}
    			depth++;
    			solve(node.left);
    			solve(node.right);
    			depth--; // 回到当前节点,深度减回来
    			return max;
  				};
  			return solve(root);
		}

4   Canvas 渲染器

//render
		class CanvasRender {
  			constructor(container) {
    			this.canvas = document.createElement('canvas');
    			this.container = container;
    			this.ctx = this.canvas.getContext('2d');
  			}
  			initSize(w, h) {
    			this.canvas.width = w;
    			this.canvas.height = h;
    			this.container.appendChild(this.canvas);
 			}
  			setStyle(ctx) {
    			ctx.font = "20px serif";
    			ctx.textAlign = "center";
    			ctx.textBaseline = "middle";
  			}
  			renderNode(x, y, r, text) {
  				this.ctx.strokeStyle="#000000"
  				this.ctx.fillStyle='#ff00ff'
    			this.ctx.beginPath();
    			this.ctx.arc(x, y, r, 0, Math.PI * 2); // 绘制圆
    			this.ctx.stroke();
    			this.setStyle(this.ctx); // 设置样式
    			this.ctx.fillText(text, x, y); // 填充节点的value
  			}
  			renderLine(x1, y1, x2, y2) {
    			this.ctx.moveTo(x1, y1);
    			this.ctx.lineTo(x2, y2);
    			this.ctx.stroke();
  			}
		}

5   树的绘制器

树的绘制器使用draw绘制二叉树的节点drawNode和连线linkNode。

//drawer
		class TreeDrawer {
 			constructor(render) {
    			this.render = render;
  			}

 		    layout(root, nodeW, nodeH) {
    			function computeWidth(root) {
      				if (!root) return 0;
      				if (!(root.left || root.right)) {
        				root.width = nodeW;
        				return root.width;
      				}
      				root.width = computeWidth(root.left) + computeWidth(root.right) + nodeW;
      				return root.width;
    				}
    				function computePosition(root, left, right, curY = nodeH) {
      					if (!root) return;
      				let x;
      				if (root.left) {
        			x = left + root.left.width + nodeW;
      				} else {
        			x = left + nodeW;
      				}
      				root.position = [x, curY];
      				computePosition(root.left, left, x, curY + nodeH);
      				computePosition(root.right, x, right, curY + nodeH);
    			}
    			computeWidth(root);
    			computePosition(root, 0, root.width);
    			return root.width;
  			}
  			

  			draw(root, nodeW = 40, nodeH = 40) {
    			let depth = getTreeDepth(root); // 获取深度
    			let width = this.layout(root, nodeW, nodeH);// canvas宽度
    			let height = depth * nodeH + nodeH; 
    			// canvas高度
    			this.render.initSize(width + nodeW, height);
    			
    			const getVector = (x, y, x1, y1) => {
      			let dis = Math.sqrt((x - x1) ** 2 + (y - y1) ** 2);
      			return [
        			(x1 - x) / dis,
        			(y1 - y) / dis
      				]
    			}
    			const linkNode = (root, child) => {
      			let [px, py] = root.position;
      			let [cx, cy] = child.position;
      			let [dx, dy] = getVector(px, py, cx, cy);
      			this.render.renderLine(px + (nodeW / 2) * dx, py + (nodeW / 2) * dy,
        		cx - (nodeW / 2) * dx, cy - (nodeW / 2) * dy)
    			}
    			
    			const drawNode = (root) => {
      				let [x, y] = root.position;
      				this.render.renderNode(x, y, nodeW / 2, root.val);
    			}
    			      
    		function draw(root) {
      			if (!root) return;
     			drawNode(root);
     			if (root.left) linkNode(root, root.left);
      			if (root.right) linkNode(root, root.right);
      			draw(root.left);
      			draw(root.right);
    		}
    		draw(root);
  			} 			                                                                       
		}

6   使用示例

// 创建一个深度为3的二叉树
const root = new FullBinaryTree(4).getRoot();
// 创建Canvas渲染器
const render = new CanvasRender(document.body);
// 创建树的绘制器
const drawer = new TreeDrawer(render);
// 绘制二叉树
drawer.draw(root);
		

此示例创建一个深度为3 的完全二叉树,并使用canvas将其绘制 。


http://www.kler.cn/a/430331.html

相关文章:

  • 高等数学学习笔记 ☞ 一元函数微分的基础知识
  • 【Arm】Arm 处理器的半主机(semihosting)机制
  • 文献综述拆解分析
  • 打开idea开发软件停留在加载弹出框页面进不去
  • Centos 下安装 GitLab16.2.1
  • 【C++】线程启动、结束与创建线程写法
  • DedeCMS最新注入漏洞(CNVD-2024-44514、CVE-2024-9076)
  • 怎么为开源项目做贡献提PR?
  • 企业经营数据分析系统:提升决策能力的利器
  • git中配置ssh的方法
  • 【计算机网络】实验15:VLAN间通信的实现方法“单臂路由”
  • 分布式事物各方案常见使用场景
  • PHP和GD库如何在图片上添加文字
  • 【IT】测试用例模版(含示例)
  • 踩坑日记-@Data注释的使用
  • 【机器学习】机器学习的基本概念、算法的工作原理、实际应用案例
  • 文生图模型开源之光!ComfyUI - AuraFlow本地部署教程
  • 如何拦截伪蜘蛛、假蜘蛛
  • 【漫话机器学习系列】002.拟合度:调整R方(Adjusted R-Squared)
  • 迅为RK3576开发板满足了4G/5G、wifi6、多网口、NPU等扩展需求
  • vue入门实战(二)父子组件显示,参数传递
  • minio参考官方文档实现多节点部署,基于ubuntu,还是失败了。。。。
  • 香港科技大学广州|智能交通学域博士招生宣讲会—同济大学专场
  • Cesium 问题: 添加billboard后移动或缩放地球,标记点位置会左右偏移
  • 设置笔记本同时连接内外网
  • 【学习总结|DAY015】Java面向对象高级-抽象类、接口