这是界面效果图:
主要的演示函数包括在一个叫TreeDemo.h的头文件中主要函数有以下内容:
接下来 就是代码的实现部分,如有兴趣请自仔细阅读:
这是主函数部分,只是完成对界面的初始化以及对Demo函数的调用
#include<stdio.h>
#include<stdlib.h>
#include "TreeDemo.h"
void CP(Node *p,char c,char n,int x, int y, int r);
int main()
{
int gdriver, gmode, i;
char path[256];
Tree *tp;
Node *np;
scanf("%s",path);
tp=CreatTree(path);
gdriver=DETECT;
initgraph(&gdriver, &gmode, "c:\\tc");
setbkcolor(BLACK); /*����ͼ�α���*/
cleardevice();
setcolor(GREEN); /*������ͼ��ɫ*/
Demo(tp);
system("pause");
closegraph();
system("pause");
return 0;
}
void CP(Node *p,char c,char n,int x, int y, int r){
p->data=c;
p->x=x;
p->y=y;
p->R=r;
p->flag=n;
}
接下来才是核心部分,所有逻辑的实现都在以下头文件中:
#include<graphics.h>
#include <math.h>
#include <conio.h>
#define LEFT 0x01
#define RIGHT 0x02
#define DRAW 0x80
#define MAXCNT 31
#define BG BLACK
#define COLOR GREEN
#define NODECOLOR BLUE
#define DR 5
#define DXY 100
#define pi 3.1415926
typedef struct node{
struct node *left;
struct node *right;
struct node *prev;
int x,y;
int high;
int num;
char flag;
char data;
char R;
}Node;
Node *prevNode=0;
int IsFind=0;
typedef struct{
Node *phead;
int currcnt;
}Tree;
Tree * CreatTree(char *path);//根据文件创建树
int AddNode(Tree *pt,char c,char data,int lr,int num);//添加节点
int DeleteNode(Tree *pt,char c,int num);//删除节点
int PreorderTra(Tree *pt);//先序遍历
int InorderTra(Tree *pt);//中序遍历
int PostorderTra(Tree *pt);//后序遍历
int PreorderL(Tree *pt);//线序线索化
int InorderL(Tree *pt);//中序线索化
int PostorderL(Tree *pt);//后续线索化
int DrawTree(Tree * pt);//绘树
int ClearTree(Tree *pt);//清楚树
void ClearDisplay(Tree *pt);//清空图像区域
int FindNode(Tree *pt,char c,int num);//寻找节点并显示过程
int Leaf(Tree *pt);//演示叶子节点
int DrawNode(Node *pn);//画节点
int ClearNode(Node *pn);//擦除节点
void TraDraw(Node * p);//画节点遍历函数
void TraClear(Node *p);//删除节点遍历函数
Node* TraAddDle(Node *p,char c,int num);//根据节点data查询节点的地址
Node * SetNode(Node *oldn,Node *newn,int lr);//创建新的节点 lr:左节点或右节点
int * GetXY(Node *p1,Node *p2,int arr[4]);//根据两节点得到连线坐标
void SwitchN(Node **p1,Node **p2);//交换两个指针的值
void DrawLine(Node *p1,Node *p2,int num);//画两个节点之间的线,num为画线方式
void ClearFlag(Node *p);//递归清楚标志位
void Trapre(Node *p);
void TraIn(Node *p);
void TraPo(Node *p);
void TrapreL(Node *p,Node *n);
void TraInL(Node *p,Node *n);
void TraPoL(Node *p,Node *n);
void TraLeaf(Node *p);
void LineShow(int x1,int y1,int x2,int y2);//动态直线
void Circleto(int x1,int y1,int x2,int y2);//动态直线(圆点)
void SLineShow(int x1,int y1,int x2,int y2);//动态曲线
void Showfun(double k,double b);//画 一个直线方程
void DrawArrow(int x1,int y1,int x2,int y2);//画一个箭头
void CleanArrow(int x1,int y1,int x2,int y2);//清楚一个箭头
int GetInput(char *pram);//读取键盘输入并在屏幕显示,解析功能返回功能号和参数
void Demo(Tree *pt);//主循环函数
Tree * CreatTree(char *path){
Tree *tp;
FILE *fp;
Node *p;
char buf[MAXCNT];
Node *nodebuf[MAXCNT];
char ch;
int cnt=0,i;
tp=(Tree *)malloc(sizeof(Tree));
if(!tp)return 0;
tp->currcnt=0;
if((fp=fopen(path,"r"))==NULL)return NULL;
ch=fgetc(fp); //从fp所指文件的当前指针位置读取一个字符
while(ch!=EOF&&cnt<31) //判断刚读取的字符是否是文件结束符
{
buf[cnt++]=ch;
ch=fgetc(fp); //继续从fp所指文件中读取下一个字符
}
fclose(fp);
//清空nodebuf
for(i=0;i<MAXCNT;i++)nodebuf[i]=0;
//为每个节点申请空间
for(i=0;i<cnt;i++){
nodebuf[i]=(Node *)malloc(sizeof(Node));
nodebuf[i]->num=i;
nodebuf[i]->data=buf[i];
nodebuf[i]->left= nodebuf[i]->right=0;
tp->currcnt++;
}
tp->phead=nodebuf[0];
tp->phead->high=1;
for( i=0;i<cnt;i++){
if(i==0){//phead
nodebuf[i]->flag=0;
nodebuf[i]->high=1;
nodebuf[i]->R=30;
nodebuf[i]->x=450;
nodebuf[i]->y=30;
nodebuf[i]->prev=0;
}
if(nodebuf[2*i+1]&&(2*i+1<31)){//有左孩子
p=nodebuf[2*i+1];
SetNode(nodebuf[i],p,0);
}
if(nodebuf[2*i+2]&&(2*i+2<31)){//有右孩子
p=nodebuf[2*i+2];
SetNode(nodebuf[i],p,1);
}
}
return tp;
}
int AddNode(Tree *pt,char c,char data,int lr,int num=-1){
Node *p,*newp;
p=TraAddDle(pt->phead,c,num);
if(!p)return 0;
if(p->high==5)return 0;
if(lr){//右
if(p->flag&RIGHT)return 0;
newp=(Node*)malloc(sizeof(Node));
if(!newp)return 0;
newp->data=data;
newp->right=newp->left=0;
SetNode(p,newp,1);
}else{//左
if(p->flag&LEFT)return 0;
newp=(Node*)malloc(sizeof(Node));
if(!newp)return 0;
newp->data=data;
newp->right=newp->left=0;
SetNode(p,newp,0);
}
//展示动画效果
DrawLine(p,newp,1);
DrawNode(newp);
return 1;
}
int DeleteNode(Tree *pt,char c,int num=-1){
Node *p;
p=TraAddDle(pt->phead,c,num);
if(!p)return 0;
setcolor(BG);
setlinestyle(0,2);
TraClear(p);
if(p==pt->phead)pt->phead=0; //如果删除的是头节点
setlinestyle(0,1);
setcolor(COLOR);
DrawTree(pt);//有时删除节点后 会对没删除节点的图像造成一些影响,所以在此刷新
return 1;
}
int PreorderTra(Tree *pt){
ClearDisplay(pt);
if(!pt->phead)return 0;
Trapre(pt->phead);
return 1;
}
int InorderTra(Tree *pt){
ClearDisplay(pt);
if(!pt->phead)return 0;
TraIn(pt->phead);
return 1;
}
int PostorderTra(Tree *pt){
ClearDisplay(pt);
if(!pt->phead)return 0;
TraPo(pt->phead);
return 1;
}
int PreorderL(Tree *pt){
if(!pt->phead)return 0;
ClearDisplay(pt);
setlinestyle(0,2);
TrapreL(pt->phead,0);
setlinestyle(0,1);
return 1;
}
int InorderL(Tree *pt){
if(!pt->phead)return 0;
ClearDisplay(pt);
setlinestyle(0,2);
TraInL(pt->phead,0);
setlinestyle(0,1);
return 1;
}
int PostorderL(Tree *pt){
if(!pt->phead)return 0;
ClearDisplay(pt);
setlinestyle(0,2);
TraPoL(pt->phead,0);
setlinestyle(0,1);
return 1;
}
int DrawTree(Tree * pt){
if(!pt->phead)return 0;
TraDraw(pt->phead);
setlinestyle(0,1);
return 1;
}
int ClearTree(Tree *pt){
if(!pt->phead)return 0;
setcolor(BG);
setlinestyle(0,2);
TraClear(pt->phead);
pt->phead=0;
setlinestyle(0,1);
setcolor(COLOR);
return 1;
}
void ClearDisplay(Tree *pt){
clearrectangle(250,0,640,360); // 清空矩形区域
ClearFlag(pt->phead);
}
int FindNode(Tree *pt,char c,int num=0){
Node *p;
ClearDisplay(pt);
DrawTree(pt);
p=TraAddDle(pt->phead,c,-1);
if(!p)return 0;
switch (num)
{
case 1:
TraInL(pt->phead,p);
break;
case 2:
TraPoL(pt->phead,p);
break;
default:
TrapreL(pt->phead,p);
break;
}
return 1;
}
int Leaf(Tree *pt){
ClearDisplay(pt);
DrawTree(pt);
if(!pt->phead)return 0;
setlinestyle(0,3);
TraLeaf(pt->phead);
setlinestyle(0,1);
return 1;
}
//*******************************NODE*******************************************
int DrawNode(Node *pn){
pn->flag|=DRAW;
setcolor(GREEN); //画圆
setfillcolor(NODECOLOR);
fillcircle (pn->x, pn->y, pn->R);
settextstyle(pn->R, 0, 0); //填充字符
setcolor(WHITE);
setbkcolor(NODECOLOR);
outtextxy(pn->x-3,pn->y-8,pn->data);
setbkcolor(BG);
setcolor(COLOR);
return 1;
}
int ClearNode(Node *pn){
pn->flag&=~DRAW;
clearcircle(pn->x, pn->y, pn->R+1); // ���Բ������
return 1;
}
void TraDraw(Node *p){
if(!p)return ;
if(p->flag&LEFT)TraDraw(p->left);
if(p->flag&RIGHT)TraDraw(p->right);
DrawNode(p);
if(p->flag&LEFT){
DrawLine(p,p->left,0);//line画线方式
}
if(p->flag&RIGHT)
{
DrawLine(p,p->right,0);
}
}
void TraClear(Node *p){
Node *preN=p->prev;
if(p->flag&LEFT)TraClear(p->left);
if(p->flag&RIGHT)TraClear(p->right);
ClearNode(p); //清楚节点图和线
if(!preN){free(p);return;}//为头节点
DrawLine(p->prev,p,0);
if(p==preN->left){
preN->flag&=~LEFT;
preN->left=0;
}else{
preN->flag&=~RIGHT;
preN->right=0;
}
free(p);
}
Node* TraAddDle(Node *p,char c,int num=-1){
Node *rp=0;
if(p->num==num||c==p->data)return p;
if(p->flag&LEFT)rp=TraAddDle(p->left,c,num);
if(rp)return rp;
if(p->flag&RIGHT)rp=TraAddDle(p->right,c,num);
if(rp)return rp;
return 0;
}
Node * SetNode(Node *oldn,Node *newn,int lr){
newn->high=oldn->high+1;
newn->flag=0;
newn->R=oldn->R-DR;
newn->y=oldn->y+(DXY+50)/(oldn->high);
newn->prev=oldn;
if(!lr){
oldn->flag|=LEFT;
oldn->left=newn;
newn->num=2*oldn->num+1;
newn->x=oldn->x-DXY/(oldn->high*oldn->high - 2*oldn->high+2);//左节点
}else{
oldn->flag|=RIGHT;
oldn->right=newn;
newn->num=2*oldn->num+2;
newn->x=oldn->x+DXY/(oldn->high*oldn->high - 2*oldn->high+2);//右
}
return newn;
}
int *GetXY(Node *p1,Node *p2,int arr[4]){
int x1,y1,x2,y2;
double k,b;
k=(double)(p2->y - p1->y)/(double)(p2->x - p1->x);
x1=p1->x;y1=p1->y;
x2=p2->x;y2=p2->y;
b=y1-k*x1;
while(((x1-p1->x)*(x1-p1->x)+(y1-p1->y)*(y1-p1->y))<(p1->R*p1->R))
{
if(x2>x1)x1++;
else x1--;
//x2==x1?
y1=k*x1+b;
}
while(((x2-p2->x)*(x2-p2->x)+(y2-p2->y)*(y2-p2->y))<(p2->R*p2->R))
{
if(x2>x1)x2--;
else x2++;
//x2==x1?
y2=k*x2+b;
}
arr[0]=x1;arr[1]=y1;arr[2]=x2;arr[3]=y2;
return arr;
}
void SwitchN(Node **p1,Node **p2){
Node *tmp=*p1;
*p1=*p2;
*p2=tmp;
}
void DrawLine(Node *p1,Node *p2,int num){
int arr[4];
void (*f[3])(int,int,int,int)={line,LineShow,SLineShow};
if(num>2)return ;
GetXY(p1,p2,arr);
f[num](arr[0],arr[1],arr[2],arr[3]);
}
void ClearFlag(Node *p){
if(!p)return ;
p->flag&=~DRAW;
if(p->flag&LEFT)ClearFlag(p->left);
if(p->flag&RIGHT)ClearFlag(p->right);
}
void Trapre(Node *p){
if(!p)return;
if((p->prev!=0)&&(p->prev->flag&DRAW))DrawLine(p->prev,p,1);
DrawNode(p);
if(p->flag&LEFT)Trapre(p->left);
if(p->flag&RIGHT)Trapre(p->right);
}
void TraIn(Node *p){
if(!p)return ;
if(p->flag&LEFT)TraIn(p->left);
if(!(p->flag&LEFT)){//左边没孩子
if(p->prev->flag&DRAW){//父节点已遍历就先画线
DrawLine(p->prev,p,1);
}
DrawNode(p);
}
else{//左边有孩子
DrawLine(p->left,p,1);
DrawNode(p);
}
if(p->flag&RIGHT)TraIn(p->right);
if(p->right&&(p->right->flag&DRAW))DrawLine(p->right,p,0);
}
void TraPo(Node *p){
if(!p)return ;
if(p->flag&LEFT)TraPo(p->left);
if(p->flag&RIGHT)TraPo(p->right);
if(p->right)DrawLine(p,p->right,0);
if(p->left)DrawLine(p->left,p,0);
DrawNode(p);
Sleep(800);
}
void TrapreL(Node *p,Node *N=0){
if(IsFind)return;
if(!p)return ;
DrawNode(p);
setcolor(WHITE);
if(prevNode)DrawLine(prevNode,p,1);
setcolor(COLOR);
if(N==p){IsFind=1;setlinestyle(0,3);DrawNode(p);setlinestyle(0,1);return;}
if(prevNode&&!(p->flag&LEFT))p->left=prevNode;//前驱
if(prevNode&&!(prevNode->flag&RIGHT))prevNode->right=p;//后继
prevNode=p;
if(p->flag&LEFT)TrapreL(p->left,N);
if(p->flag&RIGHT)TrapreL(p->right,N);
}
void TraInL(Node *p,Node *N=0){
if(!p)return ;
if(IsFind)return;
if(p->flag&LEFT)TraInL(p->left,N);
if(IsFind)return;
DrawNode(p);
setcolor(WHITE);
if(prevNode)DrawLine(prevNode,p,1);
setcolor(COLOR);
if(N==p){IsFind=1;setlinestyle(0,3);DrawNode(p);setlinestyle(0,1);return;}
if(prevNode&&!(p->flag&LEFT))p->left=prevNode;//前驱
if(prevNode&&!(prevNode->flag&RIGHT))prevNode->right=p;//后继
prevNode=p;
if(p->flag&RIGHT)TraInL(p->right,N);
}
void TraPoL(Node *p,Node *N=0){
if(!p)return ;
if(IsFind)return;
if(p->flag&LEFT)TraPoL(p->left,N);
if(p->flag&RIGHT)TraPoL(p->right,N);
if(IsFind)return;
DrawNode(p);
setcolor(WHITE);
if(prevNode)DrawLine(prevNode,p,1);
setcolor(COLOR);
if(N==p){IsFind=1;setlinestyle(0,3);DrawNode(p);setlinestyle(0,1);return;}
if(prevNode&&!(p->flag&LEFT))p->left=prevNode;//前驱
if(prevNode&&!(prevNode->flag&RIGHT))prevNode->right=p;//后继
prevNode=p;
}
void TraLeaf(Node *p){
if(!p)return ;
if(p->flag&LEFT)TraLeaf(p->left);
if(p->flag&RIGHT)TraLeaf(p->right);
if((p->flag&(LEFT|RIGHT))==0)DrawNode(p);
}
//*************************************LINE****************************************
void SLineShow(int x1,int y1,int x2,int y2){
double k=(float)(y2-y1)/(float)(x2-x1);
double b;
double k1=tan(atan(k)+pi/9),k2=tan(atan(k)-pi/9);
double db=((y2-k2*x2)-(y1-k1*x1))/(x2-x1);
double dk=pi*2/9/(x2-x1);
int flag=x2>x1?1:0;
int i=0;
int x,y;
b=y1-k1*x1;
db=db>0?db:0-db;
dk=dk>0?dk:0-dk;
x=x2;y=y2;
while(((x-x2)*(x-x2)+(y-y2)*(y-y2))<100)
{
if(flag)x--;
else x++;
y=k2*x+(y2-k2*x2);
}
circle(x1,y1,1);
circle(x2,y2,1);
moveto(x1,y1);
//Showfun(k1,y1-k1*x1);
//Sleep(100);
//Showfun(k2,y2-k2*x2);
if(flag)x1++;
else x1--;
y1=k1*x1+b;
while(atan(k)-atan(k1)<pi/9)
{
i++;
//Showfun(k1,b);
//getchar();
circle(x1,y1,1);
//getchar();
k1=tan(atan(k1)-dk);
b+=db;
if(i>1000)return;
if(flag)x1++;
else x1--;
y1=k1*x1+b;
/*if(flag)x++;
else x--;
y=k1*x+b;
if(i%2==0){
DrawArrow(x,y,x1,y1);
Sleep(30);
CleanArrow(x,y,x1,y1);
}*/
Sleep(30);
}
DrawArrow(x,y,x2,y2);
}
void LineShow(int x1,int y1,int x2,int y2){
float k;
int b;
int flagx=x2>x1?1:0;
int flagy=y2>y1?1:0;
int i=0;
moveto(x1,y1);
if(x1==x2){
while(y1!=y2){
lineto(x1,y1);
if(flagy)y1++;
else y1--;
Sleep(100);
}
return;
}
if(y1==y2){
while(x1!=x2){
lineto(x1,y1);
if(flagx)x1++;
else x1--;
Sleep(100);
}
return;
}
k=(float)(y2-y1)/(float)(x2-x1);
b=y1-k*x1;
//circle(x1,y1,1);
//circle(x2,y2,1);
while(x1!=x2&&y1!=y2)
{
i++;
if(i>1000)return;
if(flagx)x1++;
else x1--;
y1=k*x1+b;
lineto(x1,y1);
Sleep(100);
}
}
void Showfun(double k,double b){
int x=0,y=0;
y=k*x+b;
while(x<0||y<0){
y=k*x+b;
x++;
}
line(x,y,500,k*500+b);
}
void DrawArrow(int x1,int y1,int x2,int y2){
double k,b;
int x,y;
const int Alen=10;
setlinestyle(0,2);
if(y1==y2){
y=y1+Alen;
x=x1;
line(x,y,x2,y2);
y=y1-Alen;
line(x,y,x2,y2);
}else{
k=0-(x1-x2)/(y1-y2);
b=y1-k*x1;
x=x1;
y=y1;
while(((x-x1)*(x-x1)+(y-y1)*(y-y1))<Alen*Alen){
x++;
y=k*x+b;
}
line(x,y,x2,y2);
x=x1;
while(((x-x1)*(x-x1)+(y-y1)*(y-y1))<Alen*Alen){
x--;
y=k*x+b;
}
line(x,y,x2,y2);
}
setlinestyle(0,1);
}
//bug y1==y2?
void Circleto(int x1,int y1,int x2,int y2){
float k;
int b;
int flag=x2>x1?1:0;
int i=0;
if(x1==x2){
while(y1!=y2){
circle(x1,y1,1);
y1++;
}
return;
}
k=(float)(y2-y1)/(float)(x2-x1);
b=y1-k*x1;
circle(x1,y1,1);
circle(x2,y2,1);
moveto(x1,y1);
while(x1!=x2&&y1!=y2)
{
i++;
if(i>1000)return;
if(flag)x1++;
else x1--;
y1=k*x1+b;
circle(x1,y1,1);
Sleep(20);
}
}
void CleanArrow(int x1,int y1,int x2,int y2){
setwritemode(1);
DrawArrow(x1,y1,x2,y2);
setwritemode(0);
}
int GetInput(char *pram){
char ch;
int dx,db,dy;
int cnt=0,r;
char getcmd[20];
char buf[256];
char *cmd[]={
"preorder",
"inorder",
"postorder",
"preorderl",
"inorderl",
"postorderl",
"exit",
"leaf",
"find",
"del",
"insert",
0
};
char *hint[]={
"cmd:",
"preorder -no parameter",
"inorder -no parameter",
"postorder -no parameter",
"preorderl -no parameter",
"inorderl -no parameter",
"postorderl -no parameter",
"exit -no parameter",
"leaf -no parameter",
"find x p/i/o",
" (node name) (way to Traverse)",
"del x(node name)",
"insert x x r/l",
" (insert name) (new name) (right/left)",
0
};
for(cnt=0;cnt<3;cnt++)pram[cnt]=0;
settextstyle(16, 0,0);//设置字体为默认
clearrectangle(0,0,50,50);
clearrectangle(0,360,640,480); // 清空矩形区域
moveto(0,0);
dy=20;
cnt=0;
while(hint[cnt]!=0){
outtext(hint[cnt]);
cnt++;
moveto(0,dy);
dy+=20;
}
moveto(10,400);
outtext("InPut command: ");
db=0-textwidth(' ');
cnt=0;
ch=getch();
while (ch!='\r')
{
if(ch=='\b'){
if(cnt==0)goto next;//字符已被清空
cnt--;
dx=0-textwidth(buf[cnt]);
moverel(dx,0);
outtext(" ");
moverel(4*db,0);
goto next;
}
buf[cnt++]=ch;
outtext(ch);
next:
ch=getch();
}
buf[cnt]=0;
//解析命令
cnt=0;
while(buf[cnt]&&buf[cnt]!=' '){//获取命令
getcmd[cnt]=buf[cnt];
cnt++;
}
getcmd[cnt]=0;
for(r=0;cmd[r];r++){//判断命令
if(strcmp(cmd[r],getcmd)==0)break;
}
if(cmd[r]==0)return -1;//没有找到该命令
if(r<8)return r;//不需要参数
cnt=0;
while(buf[cnt++]!=' ');//跳过命令字段 到第一个空格后的第一个字符(可能为空格)
if(cnt>=strlen(buf))return r;
while(buf[cnt]==' ')cnt++;//跳过空格后的到第一参数
if(cnt>=strlen(buf))return r;
pram[0]=buf[cnt++];
while(buf[cnt]==' ')cnt++;//跳过空格后的到第一个参数
if(cnt>=strlen(buf))return r;
pram[1]=buf[cnt++];
while(buf[cnt]==' ')cnt++;
if(cnt>=strlen(buf))return r;
pram[2]=buf[cnt++];
return r;
}
void Demo(Tree *pt){
char pram[3];
int tmp=1;
while (tmp)
{
prevNode=0;
IsFind=0;
ClearDisplay(pt);
DrawTree(pt);
switch(GetInput(pram)){
case 0:
PreorderTra(pt);
break;
case 1:
InorderTra(pt);
break;
case 2:
PostorderTra(pt);
break;
case 3:
PreorderL(pt);
break;
case 4:
InorderL(pt);
break;
case 5:
PostorderL(pt);
break;
case 6:
tmp=0;
break;
case 7:
Leaf(pt);
break;
case 8:{
int num;
char p[4]="pio";
for(num=0;pram[1]!=p[num]&&p[num];num++);
if(num==3&&pram[1]!=0){outtextxy(10,420,"error pramaeter");break;}
if(!FindNode(pt,pram[0],num))
{
settextstyle(16, 0,0);//设置字体为默认
outtextxy(10,420,"no such node");
}
break;
}
case 9:
if(!DeleteNode(pt,pram[0]))
outtextxy(10,420,"Delete fail");
break;
case 10:{
int num=0;
if(pram[2]=='l')num=0;
else if(pram[2]=='r')num=1;
else {
outtextxy(10,420,"insert fail:error pramaeter");
break;
}
if(!AddNode(pt,pram[0],pram[1],num))
outtextxy(10,420,"insert fail: no such node or have child");
break;
}
default:
outtextxy(10,420,"no sunch cmd");
break;
}
getch();
}
}
康巴得!!!