收藏 分销(赏)

数据结构-二叉树遍历实验报告.doc

上传人:天**** 文档编号:4347290 上传时间:2024-09-09 格式:DOC 页数:19 大小:382.50KB
下载 相关 举报
数据结构-二叉树遍历实验报告.doc_第1页
第1页 / 共19页
数据结构-二叉树遍历实验报告.doc_第2页
第2页 / 共19页
点击查看更多>>
资源描述
数据结构之二叉树 实 验  报 告 题目:二叉树得遍历与子树交换         指导老师:杨政宇     班级:通信1202          姓名:徐江            学号:0909121127 需求分析 1. 演示程序分别用多种遍历算法遍历二叉树并把数据输出. 2. 输入字符序列,递归方式建立二叉树. 3、在演示过程序中,用户敲击键盘,输入数据,即可瞧到数据得输出. 4、实现链式存储得二叉树得多种遍历算法。 遍历算法包括: a) 中序递归遍历算法、前序递归遍历算法【选】 b) 中序遍历非递归算法 c) 先序或后序遍历非递归算法 d) 建立中序线索,并进行中序遍历与反中序遍历 5、实现二叉树得按层遍历算法 6、设计一个测试用得二叉树并创建对应得内存二叉树,能够测试自己算法得边界(包括树节点数为0、1以及>1 得不同情形)。 7、测试数据:输入数据:-+a *b —c  d  -e f 概要设计 说明:本程序在递归调用中用到了链表,在非递归调用时用到了栈。 1. 栈得抽象数据类型 ADT Stack{ 数据对象:D={ai|ai∈char,i=1,2,3……、、} 数据关系:R={〈 ai -1,ai >| ai -1,ai ∈D,i=2,3…、、} 基本操作: InitStack(&S)   操作结果:构造一个空栈 StackEmpty( S )   ﻩ初始条件:栈S已存在.     操作结果:若S为空栈,则返回OK,否则返回ERROR。 Push( &S, e )ﻫ 初始条件:栈S已存在。 操作结果:插入元素e为新得栈顶元素。   Pop( &S, &e ) ﻩ初始条件:栈S已存在且非空. ﻫ  ﻩ操作结果:删除S得栈顶元素,并用e返回其值。 GetTop( S, &e )ﻫ    ﻩ初始条件:栈S已存在且非空.     ﻩ操作结果:用e返回S得栈顶元素.  } 2、二叉树得抽象数据类型 ADT BinaryTree{  数据对象D:D就是具有相同特性得数据元素得集合。 ﻫ ﻩ 数据关系R: ﻩﻩ若D=Φ,则R=Φ,称BinaryTree为空二叉树; ﻫ ﻩﻩ若D≠Φ,则R={H},H就是如下二元关系;   (1)在D中存在惟一得称为根得数据元素root,它在关系H下无前驱;      (2)若D-{root}≠Φ,则存在D—{root}={D1,Dr},且D1∩Dr =Φ;     ﻩﻩ(3)若D1≠Φ,则D1中存在惟一得元素x1,〈root,x1〉∈H,且存在D1上得ﻩ ﻩ 关系H1 ⊆H;若Dr≠Φ,则Dr中存在惟一得元素xr,<root,xr〉∈H,且 ﻩﻩ 存在上得关系Hr ⊆H;H={<root,x1>,<root,xr>,H1,Hr}; ﻫ   ﻩﻩ(4)(D1,{H1})就是一棵符合本定义得二叉树,称为根得左子树;(Dr,{Hr})就是一 ﻩ 棵符合本定义得二叉树,称为根得右子树。 基本操作:     ﻩ CreateBiTree( &T)  ﻩ 初始条件:给出二叉树T得定义。 ﻫ    操作结果:按要求构造二叉树T。   ﻩPreOrderTraverse_re( T, print() )   初始条件:二叉树T存在,print就是二叉树全部结点输出得应用函数。    ﻩﻩ操作结果:先序递归遍历T,对每个结点调用函数print一次且仅一次.一旦ﻩﻩ ﻩ print()失败,则操作失败.    InOrderTraverse( T, print() ) ﻩﻩ初始条件:二叉树T存在,print就是二叉树全部结点输出得应用函数。    ﻩﻩ操作结果:中序非递归遍历T,对每个结点调用函数print一次且仅一次。一ﻩ ﻩ 旦printf()失败,则操作失败。  InOrderTraverse_re(T,print() )    ﻩ初始条件:二叉树T在在,print就是二叉树全部结点输出得应用函数。 操作结果:中序递归遍历T,对每个结点调用函数print一次且仅一次。一旦 ﻩﻩ  printf()失败,则操作失败。 PreOrderTraverse(T,print()) 初始条件:二叉树T存在,print就是二叉树全部结点输出得应用函数。  ﻫ 操作结果:先序非递归遍历T,对每个结点调用函数print一次且仅一次。一 ﻩ 旦print()失败,则操作失败.  Levelorder(T)  初始条件:二叉树T在在。 操作结果:分层遍历二叉树T,并输出。 InOrderThreading(Thrt,T); 初始条件:二叉树T在在. 操作结果:中序遍历二叉树,并将其中序线索化。 InOrderTraverse_Thr( T, print); 初始条件:二叉树T在在. 操作结果:中序非递归遍历二叉线索树T InThreading(p); 初始条件:结点p在在。 操作结果:结点p及子树线索化。 3、主程序得流程: void main() { ﻩ初始化; 提示; 执行二叉数ADT函数; } 4、本程序包含三个模块 1) 主程序模块 void main(){ 初始化; { 接受命令; 显示结果; } } 2) 链表模块.递归调用时实现链表抽象数据类型. 3) 栈模块。非递归调用时实现栈得抽象数据类型。 详细设计 1、宏定义及全局变量 #define TElemType char #define SElemType BiTree #define OK 1 #define OVERFLOW 0 #define ERROR 0 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 SqStack S; BiThrTree pre; BiThrTree i; 2、函数定义 int CreateBiTree(BiTree &T); ﻩ //创建二叉树 void PreOrderTraverse_re(BiTree T,void (*print)(TElemType e)); //先序递归遍历二叉树 void InOrderTraverse(BiTree T,int (*print)(TElemType e));ﻩﻩ//中序非递归遍历二叉树 void InOrderTraverse_re(BiTree T,int (*print)(TElemType e)) ; //中序递归遍历二叉树 void PreOrderTraverse(BiTree T,int (*print)(TElemType e)); //先序非递归遍历二叉树 int print(TElemType e);ﻩ //打印元素 void InitStack(SqStack &S); ﻩ //栈得初始化 void Pop(SqStack &S,SElemType &e); void Push(SqStack &S,SElemType &e); int StackEmpty(SqStack S); int GetTop(SqStack S,SElemType &e); void Levelorder(BiTree T) ; void InOrderThreading(BiThrTree &Thrt,BiThrTree T); int InOrderTraverse_Thr(BiThrTree T, int (*print)(TElemType e)); void InThreading(BiThrTree p); 3、二叉树链表 数据结构: typedef struct BiTNode{ ﻩTElemType data; ﻩstruct BiTNode *lchild ,*rchild; PointerTag LTag , RTag; }BiTNode , *BiTree , BiThrNode , *BiThrTree; 基本操作: a)构造二叉树T int CreateBiTree(BiTree &T) { ﻩchar ch; ﻩscanf("%c",&ch); ﻩif(ch==’ ’) ﻩ T=NULL; else { ﻩ if(!(T=(BiTNode *)malloc(sizeof(BiTNode)))) ﻩ return ERROR; ﻩT->data=ch; ﻩﻩif (CreateBiTree(T->lchild)) T->LTag=Link; ﻩﻩif (CreateBiTree(T—>rchild)) T—〉RTag=Link; ﻩ} ﻩreturn OK; } b)先序递归遍历二叉数T,并输出全部结点值。 void PreOrderTraverse_re(BiTree T,int (*print)(TElemType e)) { if(T) ﻩ{ ﻩif(print(T—>data)) ﻩ PreOrderTraverse_re(T—>lchild,print); ﻩﻩPreOrderTraverse_re(T—〉rchild,print); ﻩreturn ; } else return ; } c)中序非递归遍历二叉树T,并输出全部结点值 void InOrderTraverse(BiTree T,int (*print)(TElemType e)) { ﻩSqStack S; S、base=NULL;S、top=NULL; SElemType p=NULL; InitStack(S); ﻩPush(S,T); while(!StackEmpty(S)) ﻩ{ ﻩﻩwhile(GetTop(S,p)&&p) Push(S,p—>lchild); ﻩPop(S,p); ﻩﻩif(!StackEmpty(S)) ﻩ { ﻩﻩ Pop(S,p); ﻩprint(p—>data); ﻩﻩPush(S,p-〉rchild); ﻩ} ﻩ} ﻩreturn; } d)中序递归遍历二叉树T,并输出全部结点值 void InOrderTraverse_re(BiTree T,int (*print)(TElemType e)) {  if(T) {      InOrderTraverse_re(T->lchild,print);   print(T-〉data);        InOrderTraverse_re(T—>rchild,print);     }   } e)中序遍历二叉树T,并将其中序线索化,Thrt指向头结点 void InOrderThreading(BiThrTree &Thrt,BiThrTree T) { ﻩThrt=(BiThrTree)malloc(sizeof(BiThrNode)); ﻩThrt—>LTag=Link;//建头结点 Thrt->RTag=Thread; ﻩThrt-〉rchild=Thrt;//右指针回指 ﻩif(!T) ﻩThrt—〉lchild=Thrt; else { Thrt->lchild=T; pre=Thrt; ﻩﻩInThreading(T);//中序遍历进行中序线索化 ﻩﻩpre—〉rchild=Thrt; ﻩﻩpre—>RTag=Thread;//最后一个结点线索化 ﻩ Thrt->rchild=pre; } i=Thrt; }//InOrderThreading f)结点p线索化 void InThreading(BiThrTree p) {    if (p) {    ﻩInThreading(p—>lchild);   // 左子树线索化   ﻩif (!p-〉lchild) // 建前驱线索    ﻩ { p—>LTag = Thread;  p—〉lchild = pre; }    ﻩif (!pre—〉rchild) // 建后继线索    ﻩ { pre-〉RTag = Thread;  pre->rchild = p; }   pre = p;           // 保持pre指向p得前驱 ﻩInThreading(p—>rchild); // 右子树线索化   }ﻩ } // InThreading g)//中序遍历线索化二叉树 int InOrderTraverse_Thr(BiThrTree T, int (*print)(TElemType e)) { ﻩBiThrTree p=NULL; ﻩp=T—>lchild; ﻩwhile(p!=T) { while(p—〉LTag==Link) ﻩ p=p-〉lchild; if(!print(p->data)) ﻩ return ERROR; ﻩwhile(p—>RTag==Thread && p—>rchild!=T) { ﻩ p=p—〉rchild; ﻩ print(p—〉data); ﻩ } ﻩ p=p—>rchild; } ﻩreturn OK; } 4、栈 数据结构: typedef struct{ ﻩﻩSElemType *base; SElemType *top; int stacksize; }SqStack; 基本操作: a)创建一个空栈 void InitStack(SqStack &S) { S、base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType)); ﻩS、top=S、base;ﻩ //初始为空 ﻩS、stacksize=STACK_INIT_SIZE; ﻩreturn; } b)栈顶插入元素 void Push(SqStack &S,SElemType &e) { ﻩif(S、top-S、base〉=S、stacksize) ﻩ{ ﻩﻩS、base=(SElemType*)realloc(S、base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SElemType)); S、top=S、base+S、stacksize; ﻩﻩS、stacksize+=STACKINCREMENT; ﻩ} *S、top++=e; } c)栈顶删除元素 void Pop(SqStack &S,SElemType &e) { ﻩif(S、top==S、base) return; ﻩe=*--S、top; ﻩreturn; } d)判断栈就是否为空栈 int StackEmpty(SqStack S)ﻩﻩ { if(S、top==S、base) return OK; else return ERROR; } e)e返回S得栈顶元素 int GetTop(SqStack S,SElemType &e) { if(S、top==S、base) ﻩreturn ERROR; ﻩe=*(S、top—1); return OK; } 5、主函数 void main() {int flag; BiTree T; BiThrTree Thrt; printf(”******************************************************\n”); ﻩprintf(”**   实验12  二叉树得遍历     **\n”); printf(”** 1、 实现二叉树得不同遍历算法与二叉树得中序线索化算法 **\n"); printf(”** a) 中序递归遍历算法;                 **\n"); ﻩprintf("** b) 先序递归遍历算法;                 **\n"); printf(”** c) 中序遍历得非递归算法;              **\n"); ﻩprintf(”**  d) 先序或后序遍历非递归算法之一;         **\n"); printf("**  e) 建立中序线利用线索进行中序遍历与反中序遍历。 **\n”); ﻩprintf(”** 2、 实现二叉树得按层遍历算法.           **\n"); ﻩprintf("**********************************************************\n"); ﻩprintf("\n选择操作:\n\t1、先序与中序遍历算法\n\t2、中序线索得中序遍历与反中序遍历算法\n\t3、按层遍历算法\n请选择:”); ﻩscanf("%d",&flag); ﻩswitch(flag) ﻩ{   case 1: ﻩ ﻩ printf("前序递归创建二叉树(空格 表示此结点为空):\n”); ﻩﻩ getchar(); ﻩ CreateBiTree(T); ﻩ ﻩprintf("中序递归遍历输出:"); ﻩ ﻩInOrderTraverse_re(T,print); ﻩﻩprintf("\n前序递归遍历输出:”); ﻩﻩ PreOrderTraverse_re(T,print); ﻩprintf("\n中序非递归遍历输出:"); ﻩ InOrderTraverse(T,print); ﻩ ﻩ printf(”\n前序非递归遍历输出:”); ﻩ ﻩPreOrderTraverse(T,print);  ﻩﻩ printf(”\n"); ﻩﻩ break; ﻩcase 2: printf(”前序递归创建二叉树(空格 表示此结点为空):\n"); ﻩﻩﻩﻩgetchar(); ﻩ CreateBiTree(T); ﻩ ﻩprintf("\n中序遍历线索化二叉树:”); ﻩ ﻩInOrderThreading(Thrt , T); ﻩ ﻩﻩInOrderTraverse_Thr(Thrt , print); ﻩﻩﻩ break; ﻩﻩcase 3: printf(”前序递归创建二叉树(空格 表示此结点为空):\n"); ﻩ getchar(); ﻩ ﻩ CreateBiTree(T); ﻩprintf(”\n按层遍历输出:"); ﻩﻩ Levelorder(T); printf(”\n"); ﻩﻩﻩ break; ﻩﻩdefault:return; }} 6. 函数间调用关系 main InOrderTraverse_re CreateBitree PreOrderTraverse_re InOrderTraverse PreOrderTraverse InOrderThreading InOrderTraverse_Thr Threading Stack操作 调试分析 1、二叉树得分层遍历,开始时想用队列来做,但考虑到比较麻烦,因而改为数组模拟队列,简单易懂,课后可自行尝试用队列来做。 2.  在线索化二叉树时考虑到如果将两种存储结构分开将导致两个类型得指针不能互相传值,造成许多麻烦。比较两种存储结构发现,线索二叉树比二叉树多了两个标志域LTag,Rtag。于就是把两种存储结构合并为BiThrNode,并在建立二叉树时把LTag,Rtag均置为Link。程序正常运行。  3、进入演示程序BiTree、cpp,完成编译,连接(即按下Ctrl F5)进入演示界面,或直接打开执行文件BiTree、exe,产生如下图所示得界面: ⒈ 用户需根据用户提示信息操作,输入二叉树(以空格表示空结点),输入完成后按回车键,屏幕上打印出对应于该二叉树得各种遍历结果.如下图: 六、 测试结果 输入:-+a *b -c d  -e f 屏幕输出: 中序递归遍历输出:a+b*c—d 前序递归遍历输出:+a*b-cd 中序非递归遍历输出:a+b*c-d 前序非递归遍历输出:+a*b-cd 按层遍历输出:+a*b-cd 中序遍历线索化二叉树:a+b*c-d 七、 附录 BiTree、cpp BiTree、exe #include<stdio、h> #include<stdlib、h> #define QElemType BiTNode #define TElemType char #define OK 1 #define OVERFLOW 0 #define ERROR 0 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef enum PointerTag{Link,Thread};ﻩ//Link==0,指针,Thread==1,线索 typedef struct BiTNode{ TElemType data; struct BiTNode *lchild ,*rchild; ﻩPointerTag LTag , RTag; }BiTNode , *BiTree , BiThrNode , *BiThrTree; ﻩ//二叉树 #define QElemType BiTNode #define SElemType BiTree typedef struct{ SElemType *base; ﻩSElemType *top; int stacksize; }SqStack; //全局变量 SqStack S; BiThrTree pre; BiThrTree i; /*函数声明*/           int CreateBiTree(BiTree &T); ﻩ ﻩ ﻩ //创建二叉树 void PreOrderTraverse_re(BiTree T,void (*print)(TElemType e));ﻩ//先序递归遍历二叉树 void InOrderTraverse(BiTree T,int (*print)(TElemType e));ﻩ //中序非递归遍历二叉树 void InOrderTraverse_re(BiTree T,int (*print)(TElemType e)) ;ﻩ//中序递归遍历二叉树 void PreOrderTraverse(BiTree T,int (*print)(TElemType e)); ﻩ//先序非递归遍历二叉树 int print(TElemType e);ﻩ //打印元素 void InitStack(SqStack &S); //栈得初始化 void Pop(SqStack &S,SElemType &e); void Push(SqStack &S,SElemType &e); int StackEmpty(SqStack S); int GetTop(SqStack S,SElemType &e); void Levelorder(BiTree T) ; void InOrderThreading(BiThrTree &Thrt,BiThrTree T); int InOrderTraverse_Thr(BiThrTree T, int (*print)(TElemType e)); void InThreading(BiThrTree p); /*二叉树得创建递归创建*/ int CreateBiTree(BiTree &T) { ﻩchar ch; ﻩscanf("%c”,&ch); if(ch==’ ’) ﻩT=NULL; ﻩelse { if(!(T=(BiTNode *)malloc(sizeof(BiTNode)))) ﻩ ﻩreturn ERROR; ﻩT-〉data=ch; if (CreateBiTree(T—>lchild)) T->LTag=Link; ﻩif (CreateBiTree(T—〉rchild)) T->RTag=Link; ﻩ} return OK; } /*******************************************/ /*       先序递归遍历输出       */   /*******************************************/ void PreOrderTraverse_re(BiTree T,int (*print)(TElemType e)) { ﻩif(T) ﻩ{ ﻩﻩif(print(T-〉data)) ﻩ PreOrderTraverse_re(T->lchild,print); ﻩ ﻩPreOrderTraverse_re(T->rchild,print); return ; ﻩ} ﻩelse ﻩﻩreturn ; }  /*******************************************/  /*       中序非递归遍历输出     */  /*******************************************/ void InOrderTraverse(BiTree T,int (*print)(TElemType e)) { ﻩSqStack S; S、base=NULL;S、top=NULL; SElemType p=NULL; ﻩInitStack(S); Push(S,T); ﻩwhile(!StackEmpty(S)) ﻩ{ ﻩﻩwhile(GetTop(S,p)&&p) ﻩ ﻩPush(S,p->lchild); ﻩ Pop(S,p); ﻩﻩif(!StackEmpty(S)) ﻩ { ﻩ Pop(S,p); ﻩ ﻩprint(p—>data); ﻩ Push(S,p—>rchild); ﻩ } ﻩ} ﻩreturn; }  /*******************************************/  /*        中序递归遍历输出      */ /*******************************************/ void InOrderTraverse_re(BiTree T,int (*print)(TElemType e))   {   if(T) {   InOrderTraverse_re(T-〉lchild,print);   print(T—〉data);     InOrderTraverse_re(T->rchild,print);     }    return ; }  /*******************************************/ /*     按照前序非递归遍历二叉树:栈 */ /*******************************************/    void PreOrderTraverse(BiTree T,int (*print)(TElemType e))  {   SqStack S; ﻩS、base=NULL;S、top=NULL; SElemType p=T;//p指向当前访问得结点 InitStack(S);    while(p||!StackEmpty(S))   {     if(p)        {     print(p-〉data);             Push(S,p);          p=p—>lchild;      }     else          {           Pop(S,p);         p=p->rchild;       }     }  return;   } void InOrderThreading(BiThrTree &Thrt,BiThrTree T) //中序遍历二叉树T,并将其中序线索化,Thrt指向头结点 { Thrt=(BiThrTree)malloc(sizeof(BiThrNode)); Thrt-〉LTag=Link;//建头结点 ﻩThrt—>RTag=Thread; Thrt—〉rchild=Thrt;//右指针回指 ﻩif(!T) Thrt—〉lchild=Thrt; else ﻩ{ ﻩ Thrt->lchild=T; ﻩﻩpre=Thrt; ﻩﻩInThreading(T);//中序遍历进行中序线索化 ﻩﻩpre—>rchild=Thrt; ﻩﻩpre—>RTag=Thread;//最后一个结点线索化 ﻩ Thrt->rchild=pre; } i=Thrt; }//InOrderThreading void InThreading(BiThrTree p) {   if (p) { InThreading(p->lchild);  // 左子树线索化    if (!p—〉lchild) // 建前驱线索 { p-〉LTag = Thread; p-〉lchild = pre; }  if (!pre-〉rchild) // 建后继线索 { pre->RTag = Thread; pre->rchild = p; } pre = p;           // 保持pre指向p得前驱   InThreading(p->rchild); // 右子树线索化   } } // InThreading int InOrderTraverse_Thr(BiThrTree T, int (*print)(TElemType e)) //中序遍历线索化后得二叉树 { BiThrTree p=NULL; ﻩp=T->lchild; while(p!=T) ﻩ{ ﻩ while(p->LTag==Link) ﻩ p=p->lchild; ﻩﻩif(!print(p—〉data)) ﻩﻩ return ERROR; ﻩwhile(p->RTag==Thread && p->rchild!=T) ﻩ { ﻩﻩp=p—>rchild; ﻩ print(p-〉data); ﻩﻩ} ﻩp=p-〉rchild; } ﻩreturn OK; } /***************************以下为辅助函数***************************************/ int print(TElemType e) { printf("%c”,e); ﻩreturn OK; } /*栈函数*/ /*栈得初始化*/ void InitStack(SqStack &S) { ﻩS、base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType)); S、top=S、base;ﻩﻩ//初始为空 S、stacksize=STACK_INIT_SIZE; ﻩreturn; } /*栈顶插入元素*/ void Push(SqStack &S,SElemType &e) { ﻩif(S、top-S、base>=S、stacksize) ﻩ{ S、base=(SElemType*)realloc(S、base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SElemType)); S、top=S、base+S、stacksize; ﻩS、stacksize+=STACKINCREMENT; } ﻩ*S、top++=e; } /*栈顶删除元素*/ void Pop(SqStack &S,SElemType &e) { if(S、top==S、base) ﻩ return; e=*-—S、top; ﻩreturn; } int StackEmpty(SqStack S) ﻩﻩ/*若栈为空栈,则返回OK,否则返回ERROR*/ { ﻩif(S、top==S、base) ﻩreturn OK; else ﻩ return ERROR; } int GetTop(SqStack S,SElemType &e) { if(S、top==S、base) ﻩ return ERROR; ﻩe=*(S、top-1); ﻩreturn OK; }  /************************************************************/  /*       按层次顺序建立一棵二叉树        */  /************************************************************/  void Levelorder(BiTree T) {   int i,j;   BiTNode *q[20],*p; /*q[20]用于模拟队列,存储入队得结点*/    p=T; if(p!=NULL)  {    i=1;q[i]=p;j=2; } /*i为队首位置,j为队尾位置*/   while(i!=j)    { p=q[i];  
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服