? 线性表 ———————————————————————— ?li2.1两个线性表的整合 void union(List &La,List Lb){ //先求出两表的长度 ?? ?La_len=ListLength(La); ?? ?Lb_len=ListLength(Lb); ?? ?for(int i=1;i<=Lb_len;i++){ ?? ??? ?GetElem(Lb,i,e); ?? ??? ?if(!LocalElem(La,e,equal)){//Lb中的元素没有和La中的相等 ?? ??? ??? ?ListInsert(La,++La_len,e);?? ??? ? ?? ??? ?} ?? ?} }//union li2.2归并算法 //前提是两个线性表都是非递减排列
void MergeList(List La,List Lb,List &Lc){ ?? ?InitList(Lc); ?? ?int i=1,j=1,k=0; ?? ?La_len=ListLength(La); ?? ?Lb_len=ListLength(Lb); ?? ?while((i<=La_len)&&(j<=Lb_len)){ ?? ??? ?GetElem(La,i,ai); ?? ??? ?GetElem(Lb,j,bj); ?? ??? ?if(ai<=bj){ ?? ??? ? ? ? ListInsert(Lc,++k,ai); ?? ??? ? ? ? ?i++;?? ? ?? ??? ?}else{ ?? ??? ? ? ? ?ListInsert(Lc,++k,bj); ?? ??? ? ? ? ?j++; ?? ??? ?} ?? ?} ?? ?while(i<=La_len){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GetElem(La,i++,ai); ?? ??? ?ListInsert(Lc,++k,ai); ?? ?} ?? ?while(j<=Lb_len){ ?? ??? ?GetElem(Lb,j++,bj); ?? ??? ?ListInsert(Lc,++k,bj); ?? ?}?? ? }//MergeList
//动态数组 #define LIST_INIT_SIZE 100 #difine INITINCREMENT 10 typedef struct{ ?? ?ElemType *elem; ?? ?int length; ?? ?int listsize; }Sqlist Status InitList_Sq(Sqlist &L){ ?? ?L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType)); ?? ?if(!L.elem) ?? ? ? ? exit(OVERFLOW);//数据溢出 ?? ?L.length=0; ?? ?L.listsize=LIST_INIT_SIZE; ?? ?return OK; }//InistList_Sq
?li2.3线性表的元素插入 Status ListInsert_Sq(SqList &L,int i,ElemType e){ ?? ?if(i<1||i>L.length+1) ?? ??? ?return ERROR; ?? ?if(L.length>=L.Listsize){ ?? ??? ?newbases=(ElemType *)realloc(L.elem,(L.Listsize+INCREMENT)*sizeof(ElemType)); ?? ??? ?if(!newbases) ?? ??? ? ? ? ? exit(OVERFLOW); ?? ??? ?L.elem=newbases; ?? ??? ?L.listsize+=LISTINITCERMENT; ?? ?} ?? ?q=&(L.elem[i-1]); ?? ?for(p=&(L.elem[L.length-1]);p>=q;p--){ ?? ? ? ? ?*(p+1)=*p;?? ? ?? ?} ?? ?*q=e; ?? ?++L.length; ? ? ??? ?return OK; }?
li2.4线性表的元素删除 Status ListDelete_Sq(SqList &L,int i,ElemType &e){ ?? ?if(i<1||i>L.length) ?? ??? ?return ERROR; ? ?? ?p=&(L.elem[i-1]); ?? ?e=*p; ?? ?q=&(L.elem[L.length-1]) ?? ?for(++p;p<=q;++p){ ?? ??? ?*(p-1)=*p; ?? ?} ?? ?--L.length; ?? ?return OK; }//ListDelete_Sq
li2.5定位元素位置 status LocalElem_Sq(Sqlist L,int i,Status(*compare)(ElemType,ElemType)){ ?? ?i=1; ?? ?p=L.elem; ?? ?while(i<=L.length&&!(*compare)(*p++,e)) ?? ??? ?++i; ?? ?if(i<=L.length){//查找成功 ? ? ? ? ? ? ? ? ? ? ? ?return i; ?? ?}else//查找失败 ?? ? ? ? ?return 0; }//LocalElem_Sq
li2.6归并算法(指针版) void MergeList(Sqlist La,Sqlist Lb,Sqlist &Lc){ ?? ?pa=La.elem; ?? ?pb=Lb.elem; ?? ?Lc.listsize=Lc.length=La.length+Lb.length; ?? ?pc=Lc.elem=(ElemType *)malloc(Lc.listsize*sizeof(Elemtype)); ?? ?if(!Lc.elem) ?? ? ? ? exit(OVERFLOW); ?? ?pa_last=La.elem+La.length-1;//这里pa_last指的是尾指针,相当于&(La.elme[La.length-1]) ? ? ? ? ?? ?pb_last=Lb.elem+Lb.length-1; ?? ?while(pa<=pa_last&&pb<=pb_last){ ?? ??? ?if(*pa<=*pb) ?? ??? ? ? ? ?*pc++=*pa++; ?? ??? ?else ?? ??? ? ? ? ? *pc++=*pb++; ?? ?} ?? ?while(pa<=pa_last) ?? ??? ?*pc++=*pa++; ?? ?while(pb<=pb_lase) ?? ??? ?*pc++=*pb++; }//MergeList_Sq
链表 ————————————————————
li 2.7 //线性表的单链表存储结构 typedef struct LNode{ ?? ?ElemType data; ??? ?struct LNode *next; }LNode,*LinkList;
//在链表中获取元素 Status GetElem_L(LinkList L,int i,ElemType &e){ ?? ?//L为头指针 ?? ?p=L->next; ?? ?j=1;//计数器 ?? ?while(p&&j<i){ ?? ? ? ? ?p=p->next; ?? ? ? ? ? j++; ?? ?} ?? ?if(!p||j>i) ?? ? ? ?return ERROR; ?? ?else ?? ? ? ? e=p->data; ?? ?return OK; }//GetElem_L
//在链表中插入元素 Status ListInsert_L(LinkList &L,int i,ElemType e){ ? ? ? ?? ?p=L; ?? ?j=0;//这里查找元素方式不同是因为包含了是第一个元素的情况,也可以用之前查找元素的方法,但是要多加判断两种情况 ?? ?while(p&&j<i-1){ ?? ??? ?p=p->next; ?? ??? ?j++; ?? ?} ?? ?if(!p||j>i-1) ?? ? ? ? ? ?? ?return ERROR; ?? ?s=(LinkList)malloc(sizeiof(LNode)); ?? ?s->data=e; ?? ?s->next=p->next; ?? ?p->next=s; ?? ?return OK; }//LinkInsert_L
//在链表中删除元素 Status LinkDelete_L(LinkList &L,int i,ElemType &e){ ?? ?p=L; ?? ?j=0; ?? ?while((p->next)&&j<i-1){//这里因为查找的是第i-1个元素,来删除后面一个元素,所以后面一个元素必须存在,所以是p->next ?? ? ? ? ?p=p->next; ?? ? ? ? ?j++; ?? ?} ??? ?if(!(p->next)||j>i-1) ?? ??? ?return ERROR; ?? ?q=p->next; ?? ?p->next=q->next; ?? ?e=q->data; ?? ?free(q); ??? ?rerurn OK; }//LinkDelete_L
//建立链表 void CreatList_L(LinkList &L,int n){ ?? ?L=(LinkList)malloc(sizeof*(LNode)); ?? ?L->next=NULL; ?? ? ?? ?for(i=n;i>0;i--){ ?? ? ? ? ? ?? ?p=(LinkList)malloc(sizeof*(LNode)); ?? ??? ?scanf(&p->data); ?? ??? ?p->next=L->next; ?? ??? ?L->next=p; ?? ?} }//CreatList_L
//线性表的静态单链表的存储结构 #define MAXSIZE 100//链表最大长度 typedef struct{ ?? ?ElemType data; ?? ?int cur; }component,SLinkList[MAXSIZE];
//静态链表中的查找操作 int LocalElem_SL(LinkList S,ElemType e){ ?? ?i=S[0].cur; ?? ?while(i&&S[i]!=e){ ?? ??? ?i=S[i].cur; ?? ?} ?? ?return i; }//LocalElem_SL
//---------栈---------
//栈的顺序存储结构 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10//当我们实现入栈操作时,发现栈满的时候可以增加最大容量,一般用realloc()语法 typedef struct { ?? ?SElmeType *top; ?? ?SElmeType *base; ?? ?int stacksize; }Sqstack;
//初始化建立一个空的栈 Status InitStack(Sqstack &S){ ?? ?S.base = (SqElmeType *)malloc(sizeof*(SqElmeType)); ?? ?if(!S.base){ ?? ? ? ? ? ? exit(OVERFLOW); ?? ?} ?? ?S.top=S.base; ?? ?S.stacksize=STACK_INIT_SIZE; ??? ?return OK; }//InitStack
//获取栈顶元素 Status GetTop(Sqstack S,SqElmeType &e){ ?? ?//若栈不空则返回栈顶元素,反之则return ERROR; ?? ?if(S.base==S.top) return ERROR; ?? ?e=*(S.top-1); ?? ?return OK; }//GetTop
//插入元素 Status Push(Sqstack &S,SElmeType e){ ?? ?//首先要判断栈是否满 ?? ?if(S.top-S.base>=S.stacksize){//栈满 ?? ??? ?S.base = (SElemType *)realloc(S.base,(S.base+SIZEINCREMENT)*sizeof(SElmeType))); ?? ??? ?if(!S.base) exit(OVERFLOW); ? ? ? ? ? ?? ??? ?S.top=S.base+S.stacksize; ?? ??? ?S.stacksize+=STACKINCREMENT; ?? ?} ?? ?*S.top++=e; ?? ?return OK; }//Push
//删除栈顶元素 Status Pop(SqStack &S,SElmeType &e){ ?? ?if(S.top==S.base) return ERROR; ?? ?e = *(--S.top); ?? ?return OK; }//Pop
//------关于汉诺塔------ 核心思想: ?? ? ?? ?假设有塔a,b,c,此时都在塔a上,先将最上面的两个分别移到两个塔,b,c上,再将最小的那一个移到那个刚分出来的大的上面,此时空出一个塔,就可以将此时原来的塔中的最上面的移到空塔上,目的就是将最下面的 移出来,重复操作,一直要想着用我前面说的方法来移开上面的,将最下面的移出来。
//单链队列---队列的链式存储结构 //先创建节点,再创建队列 typedef struct QNode { ?? ?QElemType data; ?? ?struct QNode *next; }QNode,*QueuePtr;
typedef struct { ?? ?QueuePtr front; ?? ?QueuePtr rear; }LinkQueue;
//初始化队列 Status InitQueue(LinkQueue &Q)//默认存在队列Q{ ?? ?Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode)); ?? ?if(!Q.front) exit(OVERFLOW); ?? ?Q.front=Q.rear=NULL; ?? ?return OK; }//InitQueue
//销毁链队列 Status DestroyQueue(LinkQueue &Q){ ?? ?Q.rear=Q.front->next; ?? ?free(Q.front); ?? ?Q.front=Q.rear; ?? ?return OK; }//DestroyQueue
//插入新元素 Status EnQueue(LinkQueue &Q,QElemType e){ ?? ?p=(QueuePtr)malloc(sizeof(QNode)); ?? ?p->data=e; ?? ?p->next=NULL; ?? ?Q.rear->next=p; ?? ?Q.rear=p; ?? ?return OK; }//EnQueue
//删除队列头部的元素 Status DeQueue(LinkQueue &Q,QElemType &e){ ?? ?if(Q.front==Q.rear) ?? ??? ?return ERROR; ?? ?p=Q.front->next; ?? ?e=p->data; ?? ?Q.front->next=p->next; ?? ?if(Q.rear==p) ?? ??? ?Q.rear=Q.front; ?? ?free(p); ?? ?return OK; }//DeQueue
//---串联结----
#define MAXSTRLEN 255 #define unsigned char SString[MAXSTRLEN-1] (S[0]用来存储串的长度)
Status Concat(SString &T,SString S1,SString S2){ ?? ?if(S1[0]+S2[0]<=MAXSTRLEN){ ?? ??? ?for(int i=1;i<=S1[0];i++){ ?? ??? ??? ?T[i]=S1[i]; ?? ??? ?} ?? ??? ?for(i=1;i<=S2[0];i++){ ?? ??? ??? ?T[S1[0]+i]=S2[i]; ?? ??? ?} ?? ??? ?uncut=TURE;?? ? ?? ?} ?? ? ?? ?if(S1[0]<MAXSTRLEN){ ?? ? ? ? ? ?? ?for(int j=1;j<=S1[0];j++){ ?? ??? ??? ?T[j]=S1[i]; ?? ??? ?} ?? ??? ?for(j=1;j<=MAXSTRLEN-S2[0];j++){ ?? ??? ??? ?T[S1[0]+i]=S2[MAXSTRLEN-S2[0]]; ?? ??? ?} ?? ??? ?uncut=ERROR; ?? ?} ?? ?if(S1[0]==MAXSTRLEN){ ?? ??? ?for(int k=1;k<=MAXSTRLEN;k++){ ?? ??? ??? ?T[k]=S1[k]; ?? ??? ?} ?? ??? ?uncut=ERROR; ?? ?} ? ? ? ? ? ? ? ? return uncut; }//Concat
//----求字串--- Status SString(SString &Sub,SString S,int pos,int len){ ?? ?if(pos<1||pos>StrLength(S)||len<1||len>StrLength(S)-pos+1) ?? ??? ?return ERROR; ?? ?for(int i=1;i<len;i++){ ?? ??? ?Sub[i]=S[pos+i-1]; ?? ?} ?? ?Sub[0]=len; ?? ?return OK; }//SString
//---ADT String 的实现-- //串的堆分配表示 typedef struct { ? ??? ?char *ch; ?? ?int length; }HString; //--串的复制-- Status StrAssign(HString &T,char *chars){ ?? ?if(T.ch) { ?? ??? ?free(T.ch);?? ? ?? ?} ?? ?//求串的长度i ?? ?for(int i= 0,c=chars;c;++i,++c); ?? ?if(!i){ ?? ??? ?T.ch = NULL; ?? ??? ?T.length = 0; ?? ?}else{ ?? ??? ?if(!(T.ch=(char *)malloc(i*sizeof(char)))) ?? ??? ??? ?exit(OVERFLOW); ?? ??? ?for(int j=0;j<=i;j++){ ?? ??? ??? ?T.ch[j]=chars[j]; ?? ??? ?} ? ? ? ? ? ? ? ? } ? ? ??? ?return OK;?? ? }//StrAssign
//求串的长度 int StrLength(HString S){ ?? ?return S.length; }//StrLength
//比较串的大小 int StrCompare(HString S,HString T) { ?? ?for(int i=0;i<T.length&&i<S.length;i++) { ?? ??? ?if(S.ch[i]!=T.ch[i]) ?? ??? ??? ?return S.ch[i]-T.ch[i]; ?? ?} ?? ?return S.length-T.length; }//StrCompare
//清空串 Status ClearString(HString &S) { ?? ?if(S.ch){ ?? ??? ?free(S.ch); ?? ??? ?S.ch = NULL; ?? ?} ?? ?S.length= 0; ?? ?return OK; }//ClearString?
//拼接串 Status Concat(HString &T,HString S1,HString S2) { ?? ?if(T.ch) ?? ? ? ? ?free(T.ch); ?? ?if(!(T.ch=(char *)malloc(S1.length+S2.length)*sizeof(char))) ?? ??? ?exit(OVERFLOW); ?? ?for(int i=0;i<=S1.length-1;i++){ ?? ??? ?T.ch[i]=S1.ch[i]; ?? ?} ?? ?T.length=S1.length+S2.length; ?? ?for(i=0;i<=S2.length-1;i++) { ?? ??? ?T.ch[S1.length+i]= S2.[i]; ?? ?} ? ?? ?return OK;
}//Concat
//截取字串 Status SubString(HString &Sub,HString S,int pos ,int len) { ? ? ??? ?if(pos<1||pos>S.length||len<0||len>S.length-pos+1) ?? ??? ?return ERROR; ?? ?if(Sub.ch){ ?? ??? ?free(Sub.ch); ?? ?} ?? ?//当串为空串时 ?? ?if(!len){ ?? ??? ?Sub.ch=NULL; ?? ??? ?Sub.length=0; ?? ?}else{ ?? ??? ?if(!Sub.ch=(char *)malloc(len*sizeof(char))) ?? ??? ??? ?exit(OVERFLOW); ?? ??? ?for(int i=0;i<=len-1;i++){ ?? ??? ??? ?Sub.ch[i]=S.ch[pos+i-1]; ?? ??? ?} ?? ?} ?? ?return OK;
}//SubString?
//--串的块链存储 #define CHUNKSIZE 80 typedef struct Chunk{ ?? ?char ch[CHUNKSIZE]; ?? ?struct Chunk *next; }Chunk;
typedef struct { ? ??? ?Chunk *head,*tail; ?? ?int curlen;?? ? }LString;
//建立二叉树
//存储结构 typedef struct BiTNode { ?? ?char data; ?? ?struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; Status CreatBiTree (BiTree){ ?? ?char ch; ?? ?scanf("%c",&ch); ?? ?if(ch=="#") ?? ??? ?*T= NULL; ?? ?else{ ?? ??? ?*T=(BiTree)malloc(sizeof(BiTNode)); ?? ??? ?if(!*T) ?? ??? ??? ?exit(-1); ?? ??? ?CreatBiTree(T->lchild); ?? ??? ?CreatBiTree(T->rchild);//递归创建左右子树 ?? ?} }
//先序遍历(中序以及后序与之类似,只需要调整if中的语句顺序即可)递归算法 void Pre(BiTree T){ ?? ?if(T!=NULL){ ?? ??? ?printf("%d\t",T->data); ?? ??? ?Pre(T->lchild); ?? ??? ?Pre(T->rchild); ?? ?} }
//计算深度
int Depth(BiTree T) { ?? ?if(T==NULL) return 0; ?? ?else{ ?? ??? ?m= Depth(T->lchild); ?? ??? ?n= Depth(T->rchild); ?? ??? ?if(m>n) ?? ??? ??? ?return m+1; ?? ??? ?else ?? ??? ??? ?return n+1; ?? ?} }
//节点数计算 int NodeCount(BiTree) { ?? ?if(T==NULL) ?? ??? ?return 0; ?? ?else { ?? ??? ?return(NodeCount(T->lchild)+NodeCount(T->rchild)+1); ?? ?} }
//叶子节点计算 int LeafCount(BiTree T){ ?? ?if(T==NULL) ?? ??? ?return 0; ?? ?if(T->lchild== NULL&&T->rchild==NULL) ?? ??? ?return 1; ?? ?else{ ?? ??? ?return LeafCount(T->lchild)+LeafCount(T->rchild); ?? ?} }
//线索二叉树 //存储结构 typedef struct BiThrTree { ?? ?int data; ?? ?int ltag,rtag; ?? ?struct BiThrTree *lchild,*rchild; }BiThrTree,*BiThrTree;
//树 //每个节点的存储结构 typedef struct PTNode { ?? ?TElemType data; ?? ?int parent;//双亲位置域
}PTNode;
//树结构 #define MAX_TREE_SIZE 100 typedef struct { ?? ?PTNode nodes[MAX_TREE_SIZE]; ?? ?int r,n; }PTree;
//树结构的孩子链表形式
typedef struct CTNode { ?? ?int child; ?? ?struct CTNode *next;
}*ChildPtr;
typedef struct { ?? ?TElemType data; ?? ?ChildPtr firstchild; }CTBox;
typedef struct { ?? ?CTBox nodes[MAX_TREE_SIZE]; ?? ?int r,n; }CTree;
//树的孩子兄弟表示法的存储结构
typedef struct CSNode { ?? ?TElemType data; ?? ?struct CSNode *firstchild,*nextsibling; }CSNode,*CSTree;
//哈夫曼树
//顺序存储结构 typedef struct { ?? ?int weight; ?? ?int parent,lch,rch;
}HTNode,*HuffmanTree;
//开始建树 void CreatHuffmanTree (HuffmanTree HT,int n){
//初始化数据 ?? ?if(n<=1) return ; ?? ?m= 2*n-1; ?? ?HT= new HTNode[m+1];//这里不算C语言的语法,可以算为伪代码,用java语法来理解 ?? ?for(i= 1;i<=m;i++) { ?? ??? ?HT[i].parent= 0; ?? ??? ?HT[i].lch= 0; ?? ??? ?HT[i].rch= 0; ?? ?} ?? ?for(i= 1;i<=n;++i) { ?? ??? ?cin>>HT[i].weight; //这里也是伪代码,可与理解为输入weight的值 ?在c++中
?? ?}
?? ?for(i= n+1;i<=m;++i) { ?? ??? ?Select(HT,i-1,s1,s2);//这里可以用打擂台的方法来选出最小的两个值的索引 ,简而言之的封装 ?? ??? ?HT[s1].parent= i; ?? ??? ?HT[s2].parent= i; ?? ??? ?HT[i].weight= HT[s1].weight+HT[s2].weight; ?? ??? ?HT[i].lch=s1; ?? ??? ?HT[i].rch= s2;?? ? ?? ?} ?? ? }
//图的邻接矩阵存储结构
#define MAZINT 32767 #define MVNum 100 typedef char VerTexType; typedef int ArcType;
typedef struct { ?? ?VerTexType vexs[MVNum];//用来输入点 ?? ?ArcType arcs[MVNum][MVNum];//网时用于存储点的权值 ?? ?int vexnum,arcnum;//总的点数和边数 ?? ? }AMGraph;
Status CreatUDN(AMGraph &G) { ?? ?int quanzhi; ?? ?char v1,v2; ?? ?scanf("%c",&G.vexnum); ?? ?scanf("%c",&G.arcnum); ?? ? ?? ?for(int i=0;i<G.vexnum;i++){ ?? ??? ?scanf("%c",&vexs[i]); ?? ?} ?? ?for(int i=0;i<G.vexnum;i++) ?? ??? ?for(int j= 0;j<G.vexnum;j++) ?? ??? ??? ?arcs[i][j]=MAXINT; ?? ?for(int i=0;i<G.vexnum;i++) { ?? ??? ?scanf("%d",&quanzhi); ?? ??? ?scanf("%c",&v1); ?? ??? ?scanf("%c",&v2); ?? ??? ?i=LocateVex(G,v1); a?? ??? ?j=LocateVex(G,v2); ?? ??? ?arcs[i][j]= w; ?? ??? ?arcs[i][j]=arcs[j][i]; ?? ?} ?? ?return OK; } int LocateVex(AMGraph G,VerTexType u) { ?? ?int i; ?? ?for(i=o;i<G.vexnum;i++) { ?? ??? ?if(u==G.vexs[i]) ?? ??? ??? ?return 1; ?? ?} ?? ?return -1; }
//无向网:无方向但又权值 有向网:有方向也有权值 无向图:无方向也无权值,但是在创建是都认为权值默认为1,初始化时为0
//采用邻接矩阵的图的DFS(深度优先遍历) void DFS(AMGraph G,int v){ //v 是你输入的起始点 //如果visit数组为0则未访问,1则已经访问 ?? ?scanf("%d",&v); ?? ?visit[v]= true; ?? ?for(int w= 0;w<G.vexnum;w++) ?? ??? ?if((G.arcs[v][w]!=0)&&(!visit[w])) ?? ??? ??? ?DFS(G,w);//递归调用DFS }
//邻接表表示方法创建无向网 Status CreatUDG(ALGraph &G) { ?? ?scanf("%d%d",&vexnum,&arcsnum) //输入总结数和边数 ?? ?for(int i=0;i<G.vexnum;i++) { ?? ??? ?scanf("%c",&G.vertice[i]); ?? ??? ?G.vertice[i].firstarc=NULL; ?? ?} ?? ?//构造邻接表 ?? ?for(int j=0;j<G.arcsnum;j++) { ?? ??? ?scanf("%c%c",&v1,&v2); ?? ??? ?i=LocalVex(G,v1); ?? ??? ?j=LocalVex(G,v2); ?? ??? ?p->adjex=j; ?? ??? ?p=(ANode *)malloc(sizeof(ANode)); ?? ??? ?//尾插法 ?? ??? ?p->next=G.vertices[i].firstarc; ?? ??? ?G.vertices[i].firstarc=p; ?? ??? ?p->adjex=j; ?? ??? ?//用相同操作操作另一个对称节点 ?? ??? ?p1->next=G.vertices[j].firstarc; ?? ??? ?G.vertices[j].firstarc=p1; ?? ??? ?p1->adjex=i; ?? ??? ? ?? ?} }
|