收藏 分享(赏)

数据结构chap6.ppt

上传人:myw993772 文档编号:8798349 上传时间:2019-07-12 格式:PPT 页数:133 大小:1.87MB
下载 相关 举报
数据结构chap6.ppt_第1页
第1页 / 共133页
数据结构chap6.ppt_第2页
第2页 / 共133页
数据结构chap6.ppt_第3页
第3页 / 共133页
数据结构chap6.ppt_第4页
第4页 / 共133页
数据结构chap6.ppt_第5页
第5页 / 共133页
点击查看更多>>
资源描述

1、第六章 树和二叉树,6.1 树的类型定义,6.2 二叉树的类型定义,6.3 二叉树的存储结构,6.4 二叉树的遍历,6.6 树和森林的表示方法,6.7 树和森林的遍历,6.8 赫夫曼树与赫夫曼编码,数据对象 D:,D是具有相同特性的数据元素的集合。,若D为空集,则称为空树;否则:(1) 在D中存在唯一的称为根的数据元素root,(2) 当n1时,其余结点可分为m (m0)个互不相交的有限集T1, T2, , Tm, 其中每一棵子集本身又是一棵符合本定义的树,称为根root的子树。,数据关系 R:,基本操作:,查 找 类,插 入 类,删 除 类,Root(T) / 求树的根结点,查找类:,Val

2、ue(T, cur_e) / 求当前结点的元素值,Parent(T, cur_e) / 求当前结点的双亲结点,LeftChild(T, cur_e) / 求当前结点的最左孩子,RightSibling(T, cur_e) / 求当前结点的右兄弟,TreeEmpty(T) / 判定树是否为空树,TreeDepth(T) / 求树的深度,TraverseTree( T, Visit() ) / 遍历,InitTree(&T) / 初始化置空树,插入类:,CreateTree(&T, definition) / 按定义构造树,Assign(T, cur_e, value) / 给当前结点赋值,Ins

3、ertChild(&T, &p, i, c) / 将以c为根的树插入为结点p的第i棵子树,ClearTree(&T) / 将树清空,删除类:,DestroyTree(&T) / 销毁树的结构,DeleteChild(&T, &p, i) / 删除结点p的第i棵子树,A,B,C,D,E,F,G,H,I,J,M,K,L,A( ),树根,例如:,B(E, F(K, L),C(G),D(H, I, J(M),D( ),H,I,J(M),() 有确定的根; () 树根和子树根之间为有向关系。,有向树:,有序树:,子树之间存在确定的次序关系。,无序树:,子树之间不存在确定的次序关系。,基 本 术 语,结点

4、:,结点的度:,树的度:,叶子结点:,分支结点:,数据元素+若干指向子树的分支,分支的个数,树中所有结点的度的最大值,度为零的结点,度大于零的结点,(从根到结点的)路径:,孩子结点、双亲结点、 兄弟结点、堂兄弟 祖先结点、子孙结点,结点的层次:,树的深度:,由从根到该结点所经分支和结点构成,假设根结点的层次为1,第l 层的结点的子树根结点的层次为l+1,树中叶子结点所在的最大层次,任何一棵非空树是一个二元组Tree = (root,F) 其中:root 被称为根结点,F 被称为子树森林,森林:,是 m(m0)棵互 不相交的树的集合,A,root,F,对比树型结构和线性结构的结构特点,线性结构,

5、树型结构,第一个数据元素(无前驱),根结点(无前驱),最后一个数据元素(无后继),多个叶子结点(无后继),其它数据元素 (一个前驱、一个后继),其它分枝结点 (一个前驱、多个后继),二叉树或为空树;或是由一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成。,根结点,左子树,右子树,E,F,G,二叉树的五种基本形态:,N,L,R,L,R,空树,只含根结点,N,N,N,右子树为空树,左子树为空树,左右子树均不为空树,二叉树的主要基本操作:,查 找 类,插 入 类,删 除 类,Root(T); Value(T, e); Parent(T, e);LeftChild(T, e); Righ

6、tChild(T, e);LeftSibling(T, e); RightSibling(T, e);BiTreeEmpty(T); BiTreeDepth(T);PreOrderTraverse(T, Visit();InOrderTraverse(T, Visit();PostOrderTraverse(T, Visit();LevelOrderTraverse(T, Visit();,InitBiTree(,ClearBiTree(,二叉树 的重要特性,性质 1 : 在二叉树的第 i 层上至多有2i-1 个结点。(i1),用归纳法证明:归纳基:归纳假设:归纳证明:,i = 1 层时,只有

7、一个根结点,2i-1 = 20 = 1;,假设对所有的 j,1 j i,命题成立;,二叉树上每个结点至多有两棵子树, 则第 i 层的结点数至多 = 2i-2 2 = 2i-1 。,性质 2 : 深度为 k 的二叉树上至多含 2k-1 个结点(k1),证明:,基于上一条性质,深度为 k 的二叉树上的结点数至多为20+21+ +2k-1 = 2k-1,性质 3 : 对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1。,证明:,设 二叉树上结点总数 n = n0 + n1 + n2,又 二叉树上分支总数 b = n1 + 2n2,而 b = n-

8、1 = n0 + n1 + n2 - 1,由此, n0 = n2 + 1,两类特殊的二叉树:,满二叉树:指的是深度为k且含有2k-1个结点的二叉树。,完全二叉树:树中所含的 n 个结点和满二叉树中编号为 1 至 n 的结点一一对应。,性质 4 : 具有 n 个结点的完全二叉树的深度为 log2n +1,证明:,设 完全二叉树的深度为 k,则根据第二条性质得 2k-1 n 2k,即 k-1 log2 n k,因为 k 只能是整数,因此,k =log2n + 1,性质 5 :,若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点: (

9、1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 i/2 的结点为其双亲结点; (2) 若 2in,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点; (3) 若 2i+1n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。,6.3 二叉树的存储结构,二、二叉树的链式存储表示,一、 二叉树的顺序存储表示,#define MAX_TREE_SIZE 100 / 设二叉树的最大结点数 typedef struct ElemType *data; / 初始化时分配存储空间int nodeNum; / 二叉树中的结点数目 SqBiTree ;,一、 二叉树

10、的顺序存储表示,/ 0号单元存储根结点,例如:,A B D,1,4,0,13,2,6,(k+1)2-1 =2k+1,C,E,F,二、二叉树的链式存储表示,1. 二叉链表,2三叉链表,3双亲链表,root,结点结构:,1. 二叉链表,typedef struct / 结点结构TElemType data;struct BiTNode *lchild, *rchild; / 左右孩子指针 BiTNode, *BiTree;,结点结构:,C 语言的类型描述如下:,root,2三叉链表,结点结构:,typedef struct / 结点结构TElemType data;struct TriTNode

11、*lchild, *rchild; / 左右孩子指针struct TriTNode *parent; /双亲指针 TriTNode, *TriTree;,结点结构:,C 语言的类型描述如下:,结点结构:,3双亲链表,LRTag,L RR R L,n = 6,typedef struct / 结点结构TElemType data;int *parent; / 指向双亲的指针char LRTag; / 左、右孩子标志域 BPTNodetypedef struct / 树结构BPTNode nodesMAX_NODE_SIZE;int num_node; / 树中含结点数目int root; / 根

12、结点的位置 BPTree,一、问题的提出,二、先左后右的遍历算法,三、算法的递归描述,四、遍历算法的应用举例,顺着某一条搜索路径巡访二叉树 中的结点,使得每个结点均被访问一 次,而且仅被访问一次。,一、问题的提出,“访问”的含义可以很广,如:输出结 点的信息等。,“遍历”是任何类型均有的操作, 对线性结构而言,只有一条搜索路 径(因为每个结点均只有一个后继), 故不需要另加讨论。而二叉树是非 线性结构,,每个结点有两个后继, 则存在如何遍历即按什么样的搜索 路径进行遍历的问题。,对“二叉树”而言,可以有三条搜索路径:,1先上后下的按层次遍历; 2先左(子树)后右(子树)的遍历; 3先右(子树)

13、后左(子树)的遍历。,二、先左后右的遍历算法,先(根)序的遍历算法,中(根)序的遍历算法,后(根)序的遍历算法,根,左 子树,右 子树,根,根,根,根,根,访问根结点、遍历左子树、遍历右子树,若二叉树为空树,则空操作;否则, (1)访问根结点; (2)先序遍历左子树; (3)先序遍历右子树。,先(根)序的遍历算法:,若二叉树为空树,则空操作;否则, (1)中序遍历左子树; (2)访问根结点; (3)中序遍历右子树。,中(根)序的遍历算法:,若二叉树为空树,则空操作;否则, (1)后序遍历左子树; (2)后序遍历右子树; (3)访问根结点。,后(根)序的遍历算法:,例如:,先序序列:,中序序列:

14、,后序序列:,A B C D E F G H K,B D C A E H G K F,D C B H K G F E A,先序序列:,中序序列:,后序序列:,A B C D E F G H K,B D C A E H G K F,D C B H K G F E A,三、算法的递归描述,void Preorder (BiTree T,void( *visit)(TElemType/ 遍历右子树 ,4、查询二叉树中某个结点,四、遍历算法的应用举例,1 、统计二叉树中叶子结点的个数,2、求二叉树的深度,3、复制二叉树,5、建立二叉树的存储结构,1) 在二叉树不空的前提下,和根结点的元素进行比较,若相

15、等,则找到,返回 TRUE;,2) 否则在左子树中进行查找,若找到, 则返回 TRUE;,3) 否则继续在右子树中进行查找,若找到,则返回 TRUE,否则返回 FALSE;,4、查询二叉树中某个结点,算法基本思想:,bool Preorder (BiTree T, ElemType x, BiTree &p) /若二叉树中存在和x相同的元素,则p指向该结点并返回TRUE, / 否则 p = NULL 且返回 FALSE ,if (T) if (T-data=x) p = T; return TRUE, /if else p = NULL; return FALSE; ,else if (Pre

16、order(T-lchild, x, p) return TRUE;/else,else return(Preorder(T-rchild, x, p) ;,1、统计二叉树中叶子结点的个数,算法基本思想:,先序(或中序或后序)遍历二叉树,在遍历过程中查找叶子结点,并计数。 由此,需在遍历算法中增添一个“计数”的参数,并将算法中“访问结点” 的操作改为:若是叶子,则计数器增1。,void CountLeaf (BiTree T, int / if / CountLeaf,调用本函数之前,预置实参count为 0。,否则,整个二叉树中的叶子结点个数等于其左子树中的叶子结点个数和其右子树中的叶子结点

17、个数之和。,另一种算法思想分析方法:,若二叉树为空,则叶子结点的个数为零;,若二叉树中只有一个(根)结点,则叶子结点的个数为1;,(后序遍历),int CountLeaf (BiTree T) / 返回指针T所指二叉树中所有叶子结点个数if (!T ) return 0;if (!T-lchild /else / CountLeaf,int Count (BiTree T)if (!T ) return 0;if (!T-lchild return /else / CountLeaf,/ 返回指针 T 所指二叉树中所有结点个数,(m+n);,(m+n+1);,2、求二叉树的深度,算法基本思想:

18、,若二叉树为空树,则深度为0; 否则,二叉树的深度应为其左、右子树深度的最大值加1。 由此,需先分别求得左、右子树的深度。,首先分析二叉树的深度和它的左、右子树深度之间的关系。,(后序遍历),int Depth (BiTree T ) / 返回二叉树的深度if ( !T ) depthval = 0;else depthLeft = Depth( T-lchild );depthRight= Depth( T-rchild );depthval = 1 + (depthLeft depthRight ?depthLeft : depthRight); return depthval; ,也可以

19、从另一角度去分析:,从二叉树深度的定义还可知,二叉树的深度即为其叶子结点所在层次的最大值。由此,可通过遍历求得二叉树中所有结点的“层次”,从中取其最大值。 算法中需引入一个计结点层次的参数。,首先分析二叉树的深度和结点的“层次”间的关系。,void Depth(BiTree T , int level, int / 调用之前 level 的初值为 1。/ dval 的初值为 0.,3、复制二叉树,其基本操作为:生成一个结点。,根元素,T,左子树,右子树,NEWT,左子树,右子树,左子树,右子树,(后序遍历),BiTNode *GetTreeNode(TElemType item, BiTNod

20、e *lptr , BiTNode *rptr )if (!(T = new BiTNode)exit(1);T- data = item;T- lchild = lptr; T- rchild = rptr;return T; ,生成一个二叉树的结点 (其数据域为item,左指针域为lptr,右指针域为rptr),BiTNode *CopyTree(BiTNode *T) if (!T ) return NULL;if (T-lchild ) newlptr = CopyTree(T-lchild); /复制左子树else newlptr = NULL;if (T-rchild ) newr

21、ptr = CopyTree(T-rchild); /复制右子树else newrptr = NULL;newT = GetTreeNode(T-data, newlptr, newrptr);return newT; / CopyTree,例如:下列二叉树的复制过程如下:,newT,5、建立二叉树的存储结构,不同的定义方法相应有不同的存储结构的建立算法,以字符串的形式 “根 左子树 右子树” 定义一棵二叉树,例如:,空树,只含一个根结点的二叉树,A,以下列字符串表示,void CreateBiTree(BiTree / 构造右子树 / CreateBiTree,A B C D,A,B,C,D

22、,上页算法执行过程举例如下:,A,T,B,C,D,scanf( ,按给定的表达式建相应二叉树,表达式 = (操作数1)(运算符)(操作数2),运算符,操作数1,操作数2, 由先缀表示式建树 例如:已知表达式的先缀表示式-+ a b c / d e, 由原表达式建树 例如:已知表达式 (a+b)c d/e,例如,表示表达式 (a+b)c-d/e 的二叉树,特点:操作数为叶子结点,运算符为分支结点,先缀表示式 -+ a b c / d e,scanf( ,由先缀表示式建树的算法的基本操作:,a+b,(a+b)c d/e,a+bc,分析表达式和二叉树的关系:,(a+b)c,a b +,a b c +

23、,a b + c ,a b + c d e / -,基本操作:,scanf( ,void CrtExptree(BiTree / CrtExptree, ,switch (ch) case ( : Push(S, ch); break;case ) : Pop(S, c);while (c!= ( ) CrtSubtree( t, c); / 建二叉树并入栈Pop(S, c) break; defult : / switch, ,while(!Gettop(S, c) ,建叶子结点的算法为:,void CrtNode(BiTree ,建子树的算法为:,void CrtSubtree (Bitr

24、ee ,仅知二叉树的先序序列“abcdefg” 不能唯一确定一棵二叉树,,由二叉树的先序和中序序列建树,如果同时已知二叉树的中序序列“cbdaegf”,则会如何?,二叉树的先序序列,二叉树的中序序列,左子树,左子树,右子树,右子树,根,根,a b c d e f g,c b d a e g f,例如:,a,a,b,b,c,c,d,d,e,e,f,f,g,g,a,b,c,d,e,f,g,先序序列中序序列,void CrtBT(BiTreeelse / / CrtBT, ,if (!(T= new BiTNode) exit(OVERFLOW); T-data = preps; if (k=is)

25、 T-Lchild = NULL; else CrtBT(T-Lchild, pre, ino, ps+1, is, k-is ); if (k=is+n-1) T-Rchild = NULL; else CrtBT(T-Rchild, pre, ino, ps+1+(k-is), k+1, n-(k-is)-1 );,树的三种存储结构,一、双亲表示法,二、孩子链表表示法,三、树的二叉链表(孩子-兄弟)存储表示法,r=0 n=6,data parent,一、双亲表示法:,typedef struct PTNode Elem data;int parent; / 双亲位置域 PTNode;,#d

26、efine MAX_TREE_SIZE 100,结点结构:,C语言的类型描述:,typedef struct PTNode nodesMAX_TREE_SIZE;int r, n; / 根结点的位置和结点个数 PTree;,树结构:,r=0 n=6,data firstchild,二、孩子链表表示法:,-1000224,parent,typedef struct CTNode int child;struct CTNode *nextchild; *ChildPtr;,孩子结点结构:,C语言的类型描述:,typedef struct Elem data;ChildPtr firstchild;

27、 / 孩子链的头指针 CTBox;,双亲结点结构,typedef struct CTBox nodesMAX_TREE_SIZE;int n, r; / 结点数和根结点的位置 CTree;,树结构:,root,三、树的二叉链表 (孩子-兄弟)存储表示法,root,typedef struct CSNodeElem data;struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,C语言的类型描述:,结点结构:,森林和二叉树的对应关系,设森林F = ( T1, T2, , Tn );T1 = ( root,t11, t12, , t1m

28、 );,二叉树 B =( LBT, Node(root), RBT );,T1,T2,Tn,由森林转换成二叉树的转换规则为:,若 F = ,则 B = ;,由 ROOT( T1 ) 对应得到Node(root);,否则,,由 (t11, t12, , t1m ) 对应得到 LBT;,由 (T2, T3, Tn ) 对应得到 RBT。,由二叉树转换为森林的转换规则为:,由LBT 对应得到 ( t11, t12, ,t1m);,若 B = , 则 F = ;,否则,,由 Node(root) 对应得到 ROOT( T1 );,由RBT 对应得到 (T2, T3, , Tn)。,由此,树和森林的各种

29、操作均可与二叉树的各种操作相对应。,应当注意的是,和树对应的二叉树,其左、右子树的概念 已改变为: 左是孩子,右是兄弟,一、树的遍历,二、森林的遍历,三、树的遍历的应用,树的遍历可有2条搜索路径:,按层次遍历:,先根(次序)遍历:,后根(次序)遍历:,若树不空,则先访问根结点,然后依次先根遍历各棵子树。,若树不空,则先依次后根遍历各棵子树,然后访问根结点。,若树不空,则自上而下自左至右访问树中每个结点。,层次遍历时顶点的访问次序:,先根遍历时顶点的访问次序:,A B E F C D G H I J K,后根遍历时顶点的访问次序:,E F B C I J K H G D A,A B C D E

30、F G H I J K,层次遍历时顶点的访问次序:,先根遍历时顶点的访问次序:,A B E F C D G H I J K,后根遍历时顶点的访问次序:,E F B C I J K H G D A,A B C D E F G H I J K,1。森林中第一棵树的根结点;,2。森林中第一棵树的子树森林;,3。森林中其它树构成的森林。,可以分解成三部分:,森林,若森林不空,则 访问森林中第一棵树的根结点; 先序遍历森林中第一棵树的子树森林; 先序遍历森林中(除第一棵树之外)其余树构成的森林。,先序遍历,森林的遍历,即:依次从左至右对森林中的每一棵树进行先根遍历。,中序遍历,若森林不空,则 中序遍历森

31、林中第一棵树的子树森林; 访问森林中第一棵树的根结点; 中序遍历森林中(除第一棵树之外)其 余树构成的森林。,即:依次从左至右对森林中的每一棵树进行后根遍历。,树的遍历和二叉树遍历的对应关系 ?,先根遍历,后根遍历,树,二叉树,森林,先序遍历,先序遍历,中序遍历,中序遍历,设树的存储结构为孩子兄弟链表,typedef struct CSNodeElem data;struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,一、求树的深度,二、输出树中所有从根到叶子的路径,三、建树的存储结构,int Depth(CSTree T) /返回以

32、 T 为根指针的树的深度if (T=NULL) return 0;elseD1 = Depth(T-firstchild);D2 = Depth(T-nextsibling);/else ,return Maxd1+1,d2;,int TreeDepth( CTree T ) / T 是树的孩子链表存储结构,/ 返回该树的深度 / TreeDepth,求树的深度的算法:,if ( T.n = 0) return 0;else return Depth( T, T.r );,int Depth( CTree T, int root ) max = 0;p = T.nodesroot.firstc

33、hild;while ( p ) h = Depth( T, p-child );if ( h max ) max = h;p = p-nextchild;/whilereturn max+1; ,二、输出树中所有从根到叶子的路径的算法:,例如:对左图所示的树,其输出结果应为:,A B E A B F A C A D G H I A D G H J A D G H K,void AllPath( BiTree T, Stack / if(T) / AllPath,/ 输出二叉树上从根到所有叶子结点的路径,void OutPath( Bitree T, Stack / while / OutPa

34、th,/ 输出森林中所有从根到叶的路径,if ( !T-firstchild ) Printstack(S); else,三、建树的存储结构的算法:,和二叉树类似,不同的定义相应有不同的算法。,假设以二元组(F,C)的形式自上而下、自左而右依次输入树的各边,建立树的孩子-兄弟链表。,A,B,C,D,E,F,G,例如:,对下列所示树的输入序列应为:,(#, A) (A, B) (A, C) (A, D) (C, E) (C, F) (E, G) (#, #),A,B,C,D,(#, A),(A, B),(A, C),(A, D),(C, E),可见,算法中需要一个队列保存已建好的结点的指针,(A

35、,B), (A,C),(A,D),(B,E),(D,F),(D,G),(A,B),a,b,c,d,e,f,(A,C),(A,D),(B,E),(D,F),队列,void CreatTree( CSTree / 所建为根结点else / 非根结点的情况 / for / CreateTree, ,GetHead(Q,s); / 取队列头元素(指针值) while (s-data != fa ) / 查询双亲结点DeQueue(Q,s); GetHead(Q,s); if (!(s-firstchild) s-firstchild = p; r = p; / 链接第一个孩子结点 else r-nex

36、tsibling = p; r = p; / 链接其它孩子结点,6.8 赫 夫 曼 树 与 赫 夫 曼 编 码,最优树的定义如何构造最优树前缀编码,一、最优树的定义,树的路径长度定义为:树中每个结点的路径长度之和。,结点的路径长度定义为:从根结点到该结点的路径上分支的数目。,树的带权路径长度定义为:树中所有叶子结点的带权路径长度之和WPL(T) = wklk (对所有叶子结点),在所有含 n 个叶子结点、并带相同权 值的 m 叉树中,必存在一棵其带权路径 长度取最小值的树,称为“最优树”。,例如:,2,7 9,7,5,4,9,2,WPL(T)= 72+52+23+43+92 =60,WPL(T

37、)= 74+94+53+42+21 =89,5,4,根据给定的 n 个权值 w1, w2, , wn,构造 n 棵二叉树的集合F = T1, T2, , Tn,其中每棵二叉树中均只含一个带权值 为 wi 的根结点,其左、右子树为空树;,二、如何构造最优树,(1),(赫夫曼算法) 以二叉树为例:,在 F 中选取其根结点的权值为最小的两棵二叉树,分别作为左、 右子树构造一棵新的二叉树,并置这棵新的二叉树根结点的权值为其左、右子树根结点的权值之和;,(2),从F中删去这两棵树,同时加入刚生成的新树;,重复 (2) 和 (3) 两步,直至 F 中只含一棵树为止。,(3),(4),9,例如: 已知权值

38、W= 5, 6, 2, 9, 7 ,5,6,2,7,5,2,7,6,9,7,6,7,13,9,9,9,16,29,0,0,0,0,1,1,1,1,00,01,11,100,101,指的是,任何一个字符的编码都 不是同一字符集中另一个字符的编码 的前缀。,三、前缀编码,利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的赫夫曼编码是一种最优前缀编码,即使所传电文的总长度最短。,1. 熟练掌握二叉树的结构特性,了解相应的证明方法。2. 熟悉二叉树的各种存储结构的特点及适用范围。3. 遍历二叉树是二叉树各种操作的基础。实现二叉树遍历的具体算法与所采用的存储结构有关。掌握各种遍历策略的递归算法,灵活运用遍历算法实现二叉树的其它操作。层次遍历是按另一种搜索策略进行的遍历。,4. 熟悉树的各种存储结构及其特点,掌握树和森林与二叉树的转换方法。建立存储结构是进行其它操作的前提,因此读者应掌握 1 至 2 种建立二叉树和树的存储结构的方法。5. 学会编写实现树的各种操作的算法。6. 了解最优树的特性,掌握建立最优树和哈夫曼编码的方法。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报