前言-------------------------------------------------------------------------------------------------------------------------
leetcode有人专门画了算法的图示,我用html的画布,画出建立一个平衡二叉树的过程,抛砖引玉。
基本思路:
1 因为网页端不好执行算法,所以先用leetcode或者visual studio,算好数据
2 在网页端根据数据,画出算法的过程
若有谁有钱,可以买一个服务器,前台输入一串数字,服务器返回计算的结果,然后在页面显示出来。
甚至可以专门建一个网站,用来图示各种算法。
坐等有钱又有闲的大佬,实现网站后来@我。
步骤--------------------------------------------------------------------------------------------------------------------------
1 1. 两数之和 - 力扣(LeetCode) (leetcode-cn.com)
打开链接,复制粘贴下方的代码,并复制执行后的结果
//高度平衡的二叉树
//二叉树结点:数据域,指针域,平衡因子
struct Node
{
Node(int n=0,Node * pl=nullptr,Node * pr=nullptr):data(n),l(pl),r(pr),bf(0){}
int data;
Node *l,*r;
int bf;
};
//前向声明
class AVLTree;
ostream& operator<<(ostream& os,AVLTree& tree);
//高度平衡的二叉树类定义
class AVLTree
{
friend ostream& operator<<(ostream& os,AVLTree& tree);
public:
bool Insert(int n,int *xuanzhaun){return Insert(root,n,xuanzhaun);}
//根据{7,6,5,4,3,2,1},建立高度平衡的二叉树
//若是不平衡的二叉树,会建立出一颗单支树,搜索效率低下
AVLTree(initializer_list<int> l)
{
root=nullptr;
//0,1,2,3,4分别对应["不旋转","左旋(2,1)","先右后左(2,-1)","右旋(-2,-1)","先左后右(-2,1)"]
int XuanZhuan=0;
vector<int> xz;
cout<<"var Trees=["<<endl;
for(auto& n:l)
{
XuanZhuan=0;
Insert(n,&XuanZhuan);
xz.push_back(XuanZhuan);
//打印出一棵树
cout<<*this<<','<<endl;
}
cout<<']'<<endl;
//打印出插入过程中,每一步是否发生旋转的数组
cout<<"var XuanZhuan=[";
for(auto& n:xz)
{
cout<<n<<',';
}
cout<<"]"<<endl;
}
private:
//入参.bf=2
void RotateL(Node *& ptr)
{
//指向原来不平衡的根节点
Node * subL=ptr;
//小弟上位
ptr=subL->r;
//两个X
subL->r=ptr->l;
ptr->l=subL;
//1,2,0
ptr->bf=subL->bf=0;
}
void RotateR(Node *& ptr)
{
//指向原来不平衡的根节点
Node * subR=ptr;
//小弟上位
ptr=subR->l;
//两个X
subR->l=ptr->r;
ptr->r=subR;
//-1,-2,0
ptr->bf=subR->bf=0;
}
void RotateLR(Node *& ptr)
{
//三个结点
Node * subR=ptr,*subL=subR->l;
ptr=subL->r;
//先左旋,两个X
subL->r=ptr->l;
ptr->l=subL;
//后右旋,两个X
subR->l=ptr->r;
ptr->r=subR;
//插入新结点后,ptr->bf只可能是-1,1,如果是0不会调用该函数
//h,h-1
if(ptr->bf==-1)
{
subL->bf=0;
subR->bf=1;
}
//h-1,h
else
{
subL->bf=-1;
subR->bf=0;
}
ptr->bf=0;
}
void RotateRL(Node *& ptr)
{
//三个结点
Node * subL=ptr,*subR=subL->r;
ptr=subR->l;
//先右旋,两个X
subR->l=ptr->r;
ptr->r=subR;
//后左旋,两个X
subL->r=ptr->l;
ptr->l=subL;
//插入新结点后,ptr->bf只可能是-1,1,如果是0不会调用该函数
//h,h-1
if(ptr->bf==-1)
{
subL->bf=0;
subR->bf=1;
}
//h-1,h
else
{
subL->bf=-1;
subR->bf=0;
}
ptr->bf=0;
}
Node* root;
bool Insert(Node *& ptr,int n,int *xuanzhaun)
{
Node *pr=nullptr,*p=ptr,*q=nullptr;
int d;
stack<Node*> st;
while(p)
{
if(p->data==n) return false;
//pr是路径,也是父节点
pr=p;
st.push(pr);
if(n<p->data) p=p->l;
else p=p->r;
}
//p是叶节点的空子女域的拷贝(不是引用)
p=new Node(n);
if(pr==NULL) {ptr=p;return true;}
if(n<pr->data) pr->l=p;
else pr->r=p;
//-------------------------------------------
//只有一个结点时,根结点bf=0
//后续每次添加结点,需要更新父节点路径的bf
//新添加的叶节点的bf是0,其无子女
while(!st.empty())
{
pr=st.top();
st.pop();
if(p==pr->l) pr->bf--;
else pr->bf++;
if(pr->bf==0) break;//pr高度没变
//pr高度变了,但还是平衡的,继续考察父节点
else if(pr->bf==1 || pr->bf==-1)
p=pr;
else if(pr->bf==2)
{
//同号单旋
if(p->bf==1)
{
RotateL(pr);
*xuanzhaun=1;
}
else
{
RotateRL(pr);
*xuanzhaun=2;
}
break;
}
else if(pr->bf==-2)
{
//同号单旋
if(p->bf==-1)
{
RotateR(pr);
*xuanzhaun=3;
}
else
{
RotateLR(pr);
*xuanzhaun=4;
}
break;
}
}
//while跳出的时机不同
if(st.empty())
ptr=pr;
else
{
q=st.top();
if(q->data>pr->data) q->l=pr;
else q->r=pr;
}
return true;
}
};
//子女域存放子女在输出数组中的索引
ostream& operator<<(ostream& os,AVLTree& tree)
{
int index=1;
cout<<'[';
//层次遍历,只有非空的指针可以入栈
queue<Node*> q;
Node* temp=nullptr;
if(tree.root)
q.push(tree.root);
while(!q.empty())
{
//temp一定非空
temp=q.front();
cout<<"{data:"<<temp->data<<','<<"bf:"<<temp->bf<<",l:";
if(temp->l)
{
cout<<index++<<",r:";
q.push(temp->l);
}
else
{
cout<<-1<<",r:";
}
if(temp->r)
{
cout<<index++<<"},";
q.push(temp->r);
}
else
{
cout<<-1<<"},";
}
//出一个,进一大堆
q.pop();
}
cout<<']';
return os;
}
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//算法的输入------------------------------------------------------------------
initializer_list<int> l{7,6,5,4,3,2,1};
//图示所需的数据---------------------------------------------------------------
//var RawInput=[7,6,5,4,3,2,1]
cout<<"var RawInput=[";
for(auto & n:l)
cout<<n<<',';
cout<<']'<<endl;
//var Trees ,var XuanZhuan
AVLTree MyAVLTree(l);
vector<int> laji;
return laji;
}
};
2 新建AVLTree.html,复制粘贴下方的代码,并把leetcode的代码执行结果替换到相应的位置
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>RBTree</title>
<style>
.InputWihth
{
width:50px;
}
</style>
<script>
//图示输入:来自leetcode或者VS
var RawInput=[7,6,5,4,3,2,1,]
var Trees=[
[{data:7,bf:0,l:-1,r:-1},],
[{data:7,bf:-1,l:1,r:-1},{data:6,bf:0,l:-1,r:-1},],
[{data:6,bf:0,l:1,r:2},{data:5,bf:0,l:-1,r:-1},{data:7,bf:0,l:-1,r:-1},],
[{data:6,bf:-1,l:1,r:2},{data:5,bf:-1,l:3,r:-1},{data:7,bf:0,l:-1,r:-1},{data:4,bf:0,l:-1,r:-1},],
[{data:6,bf:-1,l:1,r:2},{data:4,bf:0,l:3,r:4},{data:7,bf:0,l:-1,r:-1},{data:3,bf:0,l:-1,r:-1},{data:5,bf:0,l:-1,r:-1},],
[{data:4,bf:0,l:1,r:2},{data:3,bf:-1,l:3,r:-1},{data:6,bf:0,l:4,r:5},{data:2,bf:0,l:-1,r:-1},{data:5,bf:0,l:-1,r:-1},{data:7,bf:0,l:-1,r:-1},],
[{data:4,bf:0,l:1,r:2},{data:2,bf:0,l:3,r:4},{data:6,bf:0,l:5,r:6},{data:1,bf:0,l:-1,r:-1},{data:3,bf:0,l:-1,r:-1},{data:5,bf:0,l:-1,r:-1},{data:7,bf:0,l:-1,r:-1},],
]
var XuanZhuan=[0,0,3,0,3,3,3,]
//手动变量-------------------------------------
var XCoordinate=300;
var YCoordinate=100;
var NodeRadius=20
//初始化变量-------------------------------------
var DataPosAdjust=NodeRadius/4
var XOffSet=50
var YOffSet=50
var CurNewNode=1
var TreeNode=Trees[CurNewNode-1]
var NewNode=RawInput[CurNewNode]
var TreeNode2=Trees[CurNewNode]
//流程变量-------------------------------------
var i=0;
var j=0;
var k=0;
var NodeStr="";
var MinNewNode=1
var MaxNewNode=RawInput.length-1
var XuanZhuanText=["不旋转","左旋(2,1)","先右后左(2,-1)","右旋(-2,-1)","先左后右(-2,1)"]
function Previous() {
if(CurNewNode>MinNewNode)
{
CurNewNode--;
TreeNode=Trees[CurNewNode-1]
TreeNode2=Trees[CurNewNode]
ctx.fillStyle="azure";
ctx.fillRect(0,0,1200,600);
ctx.fillStyle="black";
PreOrder(TreeNode,0,XCoordinate,YCoordinate)
PreOrder(TreeNode2,0,XCoordinate+600,YCoordinate)
NewNode=RawInput[CurNewNode]
document.getElementById("NewNode").innerHTML="NewNode: "+NewNode+" "+XuanZhuanText[XuanZhuan[CurNewNode]];
}
}
function Next() {
if(CurNewNode<MaxNewNode)
{
CurNewNode++;
TreeNode=Trees[CurNewNode-1]
TreeNode2=Trees[CurNewNode]
ctx.fillStyle="azure";
ctx.fillRect(0,0,1200,600);
ctx.fillStyle="black";
PreOrder(TreeNode,0,XCoordinate,YCoordinate)
PreOrder(TreeNode2,0,XCoordinate+600,YCoordinate)
NewNode=RawInput[CurNewNode]
document.getElementById("NewNode").innerHTML="NewNode: "+NewNode+" "+XuanZhuanText[XuanZhuan[CurNewNode]];
}
}
</script>
</head>
<body>
<canvas id="myCanvas" width="1200" height="600" style="border:1px solid #c3c3c3;background-color: azure;">
您的浏览器不支持 HTML5 canvas 标签。
</canvas>
<p align="center">
<span><button type="button" onclick="Next()">Next</button></span>
<span><button type="button" onclick="Previous()">Previous</button></span>
<span id="NewNode">垃圾</span>
</p>
<script>
//依次画根结点,左子女,右子女
//前序遍历树并
function PreOrder(tree,index,x,y)
{
//结点
ctx.beginPath();
ctx.arc(x,y,NodeRadius,0,2*Math.PI);
ctx.stroke();
//数据域
ctx.font="10px Arial";
NodeStr=""
NodeStr+=tree[index].data
NodeStr+=':'
NodeStr+=tree[index].bf
ctx.fillText(NodeStr,x-DataPosAdjust-5,y+DataPosAdjust);
if(index==0)
{
//左子树
if(tree[index].l!=-1)
{
//画指针
ctx.moveTo(x-NodeRadius/1.414,y+NodeRadius/1.414);
ctx.lineTo(x-XOffSet-100+NodeRadius/1.414,y+YOffSet-NodeRadius/1.414);
ctx.stroke();
PreOrder(tree,tree[index].l,x-XOffSet-100,y+YOffSet);
}
//右子树
if(tree[index].r!=-1)
{
//画指针
ctx.moveTo(x+NodeRadius/1.414,y+NodeRadius/1.414);
ctx.lineTo(x+XOffSet+100-NodeRadius/1.414,y+YOffSet-NodeRadius/1.414);
ctx.stroke();
PreOrder(tree,tree[index].r,x+XOffSet+100,y+YOffSet);
}
}
else
{
//左子树
if(tree[index].l!=-1)
{
//画指针
ctx.moveTo(x-NodeRadius/1.414,y+NodeRadius/1.414);
ctx.lineTo(x-XOffSet+NodeRadius/1.414,y+YOffSet-NodeRadius/1.414);
ctx.stroke();
PreOrder(tree,tree[index].l,x-XOffSet,y+YOffSet);
}
//右子树
if(tree[index].r!=-1)
{
//画指针
ctx.moveTo(x+NodeRadius/1.414,y+NodeRadius/1.414);
ctx.lineTo(x+XOffSet-NodeRadius/1.414,y+YOffSet-NodeRadius/1.414);
ctx.stroke();
PreOrder(tree,tree[index].r,x+XOffSet,y+YOffSet);
}
}
}
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
PreOrder(TreeNode,0,XCoordinate,YCoordinate)
PreOrder(TreeNode2,0,XCoordinate+600,YCoordinate)
document.getElementById("NewNode").innerHTML="NewNode: "+NewNode+" "+XuanZhuanText[XuanZhuan[CurNewNode]];
</script>
</body>
</html>
3 在浏览器打开AVLTree.html
输入是{7,6,5,4,3,2,1}
插入5时,本来是单支树,7(平衡因子-2)->6(-1)->5
(-2,-1)满足书中,右旋的条件
右旋之后树平衡了

本文通过HTML5画布展示如何使用AVLTree算法在LeetCode题目中构建平衡二叉树,包括插入过程中的旋转操作和可视化结果。博主分享了从数据预处理到网页动态展示的完整步骤,期待大佬实现在线演示平台。
2174

被折叠的 条评论
为什么被折叠?



