线性表的存储以及相关操作实现

//以前的实验报告被我找到了,找知道还在就不用QQ空间上的截图了,代码很糟糕,那是因为本人还是菜鸟

一,顺序存储

内容:此线性表采用顺序存储,实现了初始化、建表、查找、删除、打印,清空,销毁,返回前去后继等功能。

---------------函数功能实现------------------
void InitList(sqList *L)
{
	(*L).elem = (int*)malloc(LIST_INIT_SIZE*sizeof(int));
        //申请空间,将首地址赋给表指针作为基址
	if(!(*L).elem) exit(0);//如果申请失败就退出
	(*L).length =0;//表长为空,无数据
	(*L).listsize =LIST_INIT_SIZE;//容量赋值
}//创建空表
void creatlist(sqList *L)
{
	int n,x,i;
	printf("请输入要存储的数据个数\n");
	scanf("%d",&n);
	printf("请输入数据:\n");
	for(i=1;i<=n;i++)//循环读入数据
	{
		scanf("%d",&x);
		(*L).elem[i] = x;
	}
	(*L).length = n;//别忘了为表长赋值
}//输入数据
int destroyList(sqList *L){
  int i;
  if ((*L).elem)//线性表存在,则继续执行
  {
      free((*L).elem);
      (*L).elem = NULL;
      printf("\n\n--线性表销毁完毕--\n");
  }
  else {printf("\n\n--表不存在--\n");return FALSE;}//否则错误退出
   return OK;
}//销毁线性表

void clearlist(sqList *L){
    (*L).length=0;                //将线性表的长度置为0
    printf("\n\n--线性表清空完毕--\n");//清空即是让数据清空,表长为0即是无数据(可看上面)
}//清空线性表

int ListEmpty(sqList *L){
  if ((*L).length==0)//同样道理,通过判定表长便可以确定线性表是否为空
  {
      printf("\n\n线性表为空!!\n");
      return TRUE;
  }
  else
  {
      printf("\n\n线性表不为空!!\n");
      return FALSE;
  }
}//判断线性表是否为空
int ListLength(sqList *L){
        printf("\n\n--线性表长度为:%d--\n",(*L).length);//没啥说的,直接返回线性表成员length的大小即可
	return ((*L).length);
}//返回线性表长度
int GetElem(sqList *L,int i,int e)
{
  if (i<1||i>(*L).length) {return 0;}
   //判断i值是否合理,若不合理,返回ERROR
  else e = (*L).elem[i];//位序正确,即把对应的线性表成员中的元素elem[i]作为返回值
  return e;
}//返回特定位序的元素


int LocateELem(sqList *L,int i,int e)
{
  for (i=1;i <= (*L).length;i++)//结合上面的输入数据(creatlist)的函数,从1开始遍历
      if ((*L).elem[i]==e) return i;//找到则返回1
  if(i = (*L).length&&(*L).elem[i] != e)
      return ERROR;//找不到则返回0
}//定位特定元素的位序
int priorelem(sqList *L,int cur_e,int pre_e){
    int i;
	if((*L).elem == NULL)//判断线性表是否存在,不存在则退出
	{
		printf("--表不存在--\n");
		exit(0);
	}
   for(i=1;((*L).elem[i]!=cur_e)&&(i<=(*L).length);i++);//利用for循环遍历
   //循环出来之后即是elem[i]=cur_e	    
   if(i<=(*L).length)
   printf("\n--当前位序:%d--\n",i);//方便查看当前位置
   else
   {printf("\n--位序过界--\n");return(FALSE);}

   if((*L).elem[i] == cur_e)
	   pre_e = (*L).elem[i-1];//记录位序的前一个元素数据
   else
	   {printf("\n--%d不存在于表中--\n",cur_e);return (FALSE);}
   return pre_e;
}//返回特定元素的前驱
int nextelem(sqList *L,int cur_e,int next_e){
	int i=1;
	
        if((*L).elem == NULL)
	{
		printf("--is null--\n");
		exit(0);
	}
	for(i=1;(*L).elem[i]!=cur_e&&i<=(*L).length;i++);
	if(i<=(*L).length)
            printf("\n--当前位序:%d--\n",i);
	if(i<(*L).length)
	     next_e = (*L).elem[i+1];
	else
	   {printf("\n--%d的后继不存在--\n",cur_e);return (FALSE);}
	   return next_e;
}//返回特定元素的后继
int ListInsert_sq(sqList *L,int i,int e)
{
  if(i < 1||i > (*L).length+1)  {
  printf("\n--位序不存在,插入错误--\n");return ERROR;}
    if((*L).length >= (*L).listsize){
        newbase=(int*)realloc((*L).elem,
                 ((*L).listsize+LISTINCREMENT)*sizeof(int));
       //空间不足,则增加空间然后将数据复制入新的存储空间
       if(!newbase) exit(0);//空间申请失败,则退出
       (*L).elem = newbase;//新基址(首地址)
       (*L).listsize += LISTINCREMENT; //增加存储容量
    }
    q = &((*L).elem[i]);//q为插入位置
    ++(*L).length;//关键:先将空间增一,那么length-1即是原表的最后一个位置
    for(p = &((*L).elem[(*L).length-1]);p>=q;--p) *(p+1) = *p;//从(原)表尾开始右移
    *q = e;
    return OK;}

int ListDelete_sq(sqList *L,int i,int e){
  if(i <1||i>(*L).length )  {
  printf("\n--位序不存在,无法删除--\n");return -1;}
    p = &((*L).elem[i]);//被删除元素位置
    e = *p;//将被删除的元素的值赋给e
    q = (*L).elem + (*L).length;//表尾元素的位置
    for(++p;p <= q;++p) *(p-1) = *p;//被删除元素的下一个开始左移
    --(*L).length;
    return e;
}//删除位序i的元素,并用e返回其值
int ListTraverse(sqList *L){
   int i;
   printf("\n列表如下:\n\n");
   printf("位序 | ");
   for(i=1;i<=(*L).length;i++)
	{  if((*L).elem[i]<100)
       printf("%2d ",i);
	   else if( (*L).elem[i] <1000)
       printf("%3d ",i);
	}
   printf("\n——————————————————————\n");
   printf("数据 | ");
    for(i=1;i<=(*L).length;i++)//格式对齐采用不同精度
	{  if((*L).elem[i]<100)
       printf("%2d ",(*L).elem[i]);
	   else if( (*L).elem[i] <1000)
       printf("%3d ",(*L).elem[i]);
	}
    printf("\n");
   return OK;
}
——————————顺序表主函数(测试)————————————
void main()
{
    int cur_e;
    int pre_e;
    int next_e;
    int e;
    int i;
    sqList myl;
    /*测试函数*/
  sqList *L = &myl;//申请指针空间
  InitList(L);//创建线性表
    creatlist(L);//测试输入
    ListTraverse(L);//输出查看
    /*前驱函数*/
    printf("\n\n请输入表内数据,系统将会找出其的前驱:\n");
    scanf("%d",&cur_e);
    pre_e = priorelem(L,cur_e,pre_e);
	if(pre_e != FALSE)
    printf("--%d的前驱是:%d--",cur_e,pre_e);
    /*后继函数*/
    printf("\n\n请输入表内数据,系统将会找出其的后继:\n");
    scanf("%d",&cur_e);
    next_e = nextelem(L,cur_e,next_e);
	if(next_e != FALSE)
    printf("--%d的后继是:%d--",cur_e,next_e);
    /*表长函数*/
    ListLength(L);
    /*查找元素输出位序函数*/
    printf("\n\n请输入你要查找的元素位序:\n");
    scanf("%d",&i);
    e = GetElem(L,i,e);
	if(e == 0)
    printf("\n\n--线性表中不包含该位序,查找失败--\n");
	else
    printf("\n--线性表位序%d处的元素为:%d--\n",i,e);
    /*定位元素位序函数*/
    printf("\n\n请输入你要查找的元素,系统将返回它的位序:\n");
    scanf("%d",&e);
    i = LocateELem(L,i,e);
    if(i == 0)
    printf("\n\n--线性表中找不到该元素,查找失败--\n");
    else
    printf("\n\n该元素的位序为:%d\n",i);
    /*插入函数*/
    printf("\n\n请输如你要插入的数据和该数据插入的位置:\n");
    scanf("%d%d",&e,&i);
    ListInsert_sq(L,i,e);
    ListTraverse(L);
    /*删除函数*/
    printf("\n\n请输入你要删除的位序:\n");
    scanf("%d",&i);
    e = ListDelete_sq(L,i,e);
	if(e != -1)
    printf("\n\n--位序%d的元素:%d已被删除--\n",i,e);
    ListTraverse(L);
}

二.链式存储

内容:此线性表采用链式存储,实现了初始化、建表、查找、删除、打印,清空,销毁,返回前去后继等功能。

DAT *InitList(DAT *head)//创建空表
{
	head = (DAT *)malloc(LEN);
	if(!head)
	{printf("\n--链表创建失败--\n");exit(FALSE);}
	head ->next = NULL;
	return head;
}
DAT *creatList(DAT *head)//数据录入
{
	DAT *p1,*p2;
	char ch;
	int n = 1;
	head = (DAT *)malloc(LEN);
	p1 = p2 = (DAT *)malloc(LEN);
	if(p1 != NULL)
	{
	    printf("\n请输入数据:\n");
	    scanf("%d",&p1->data);//p1每一次存储都需要申请空间
	    head -> next = p1;//注意:本程序的头结点不存储数据		    //p2 = p1;为什么这步可以省略呢?就是因为p1,p2在申请空间的时候被赋予的地址是一致的!
		printf("\n继续录入(y/any other keys to exit)?\n");
		getchar();
		scanf("%c",&ch);
		while(ch == 'Y'||ch == 'y')
		{
			printf("\n请输入数据:\n");
			p1 = (DAT *)malloc(LEN);
			if(p1 != NULL)
                            scanf("%d",&p1->data);
			p2 -> next = p1;
			p2 = p1;
			printf("\n继续录入(y/any other keys to exit)?\n");
			getchar();
	        scanf("%c",&ch);
		}
		p2 -> next = NULL;//p2作为连接新建节点的指针,如果next为空,那自然是结束了录入
		printf("\n--录入结束--\n");
	}
	return head;
}
 int ClearList(DAT *head) //保留头结点,其余节点数据清空
 {
   if(head == NULL)
   {
	   printf("\n--表不存在--\n");
	   return FALSE;
   }
   DAT *p,*q;
   p = head -> next;
   while(p)
   {
     q=p->next;//用声明的另一个指针q来记录p的下一节点的位置
	 printf("\n--已删除表中数据:%d--\n",p -> data);
	 free(p);
	 p=q;//结合q = p -> next,此语句的作用即是让p往后移动,从而实现逐个清空数据的目的
   }//直至p为空的时候便是结束了循环,清空结束
   head -> next = NULL; //数据清空之后头结点的指针域为空
   return TRUE;
 }
 DAT *destroyList(DAT *head)//销毁 {
     if(head == NULL)
	 {
	   printf("\n--表不存在,销毁无效--\n");
	   exit(FALSE);
	 }
     DAT *p;
	 p = head;
     while(head)
	 {

		 p = head -> next;
		 free(head);//第一次循环的时候清除了头结点
		 head = p;
	 }
	 printf("\n--链表销毁结束--\n");
	 return head;
 }
 int ListLength(DAT *head)
{
	DAT *p;
	int i = 0;
	p = head -> next;
	while(p)
	{
		i++;
		p = p -> next;
	}
    printf("\n--链表长度为:%d--\n",i);
	return i;
}
void ListTraverse(DAT *head)//输出函数
{
	DAT *p = head -> next;
	DAT *q = p;//位序输出遍历需要
	int n = 0;
	int length;
	length = ListLength(head);
	if(p)
	{
		printf("\n位序 | ");
		for(n = 1;n <= length;n++)
		{
			   if(q -> data <10)
			   printf("%d ",n);
			   else if(q -> data <100)
			   printf("%2d ",n);
			   else 
			   printf("%3d ",n);
			   q = q -> next;
		}
		printf("\n------------------------------------\n");
		printf("数据 | ");
		do
		{
			printf("%d ",p -> data);
			p = p -> next;
		}
		while(p);
	}
	printf("\n");
}
int GetElem(DAT *head,int i,int e)
{
     int n = 1;
     DAT *p;
     p = head ->next;
     while(p && n < i )//当p不为空且初始位序小于查找位序的时候
     {
		 p = p -> next;//指针后移直至与查找位序相等为止推出循环
		 n++;
	 }
	 if(!p || n > i)
	 {printf("\n--找不到该位序--\n");return FALSE;}
	 e = p -> data;
	 return e;
}
int LocateElem(DAT *head,int e)//查找e的位序,并将其位序返回
{
	DAT *p;
	int i = 1;
	printf("\n请输入你要查找的数据:\n");
	scanf("%d",&e);
        p = head -> next;
	while(p)
	{
         	if(p -> data == e)
		{
			printf("\n--%d的位序为:%d--\n",e,i);
			return i;
		}
		else
		p = p -> next;
		i++;
	}
	printf("\n--找不到该元素--\n");	return 0;
}
int PirrorElem(DAT *head,int cur_e,int pre_e)//查找元素的前驱
{
	printf("\n请输入表中数据,系统将会返回其的前驱:\n");
	scanf("%d",&cur_e);
	if(head == NULL)
	{
		printf("\n--表不存在--\n");
		return FALSE;
	}
	DAT *p,*q;
	q = head -> next;//q指向第一个节点
        while(p)
       {
	 p = q -> next;//p指向q的下一节点

	 if( q -> data == cur_e)//cur_e为第一个元素的时候,提示错误
	{
		printf("\n--表中第一个元素是没有前驱的,无法查找--\n");
                return FALSE;
	}
        if(p -> data == cur_e)//如果p指针找到了cur_e,就用q返回前驱
		{
			pre_e = q -> data;
			printf("\n--%d的前驱是:%d--\n",cur_e,pre_e);
			return pre_e;
		}
	else
	{
		if( p -> next != NULL)
		q = p;//p,q指针相连,q后移也会带动p的后移
		else// p遍历结束达到NULL的时候即是没有找到输入的数据,提示错误
		{
			printf("\n--表中无此数据--\n");
	                return FALSE;
		}

	}
     }
}
int NextElem(DAT *head,int cur_e,int next_e)//查找元素的后继
{
	DAT *p,*q;
	printf("\n请输入数据,系统将会返回其的后继:\n");
	scanf("%d",&cur_e);
	p = head -> next;
	while(p)
	{
		q = p -> next;
		if( p -> data == cur_e && p -> next != NULL)//p没有下一节点的话,也就没有后继之说了
		{
			next_e = q -> data;
			printf("\n-%d的后继为:%d--\n",cur_e,next_e);
			return next_e;
		}
		if( p -> data != cur_e)
		{
			p = q;//p -> next 不为空的时候,p,q后移遍历
			if( p -> next == NULL && p -> data == cur_e )//判断是否有后继
			{
				printf("\n--末位数据无法查找后继--\n");
				return FALSE;
			}
			if( p -> next == NULL && p ->data != cur_e)//判断时候存在cur_e
			{
				printf("\n--表中不存在该元素--\n");
				return FALSE;
			}
		}
	}
}
DAT *ListInsert(DAT *head,int i,int e)//按位序i插入特定元素e
//定义结构体指针函数,用于返回结构体指针head
{
	printf("\n请输入插入的位序和元素\n");
	scanf("%d%d",&i,&e);
	DAT *p = head;//和下面n = 0相对应,head无数据
	DAT *q;
	int n = 0;
	while(p != NULL && n < i-1)//找到i的前一个节点
	{
		p = p -> next;//循环第一次的时候p便指向了第一个节点,和n=1对应
		n++;//n = 1
	}//出循环时n=i-1,p也就指向了i的前一位序
	if(p == NULL || n > i-1)//i过大(插入的位序的前一个如果还是空的话,那就超出了插入范围)或过小的时候报错,退出
	{
		printf("\n--位序错误,插入失败--\n");
		return head;
	}
        q = (DAT *)malloc(LEN);//新节点空间申请
	q -> data = e;//新节点赋值
	q -> next = p->next;//新节点定位(p -> next为第i个节点),将新节点与原表中第i个节点相连,即是替换了第i的位置
	//如果是表尾插入的话,q -> next == NULL
	p -> next = q;//再将新节点与前面的p节点相连,即完成了插入
	printf("\n--%d已添加到表中第%d位序--\n",e,i);
	return head;//返回头指针,方便打印链表
}
DAT *ListInsert_last(DAT *head,int e)//表尾插入函数
//定义结构体指针函数,用于返回结构体指针head
{
	int leng = ListLength(head);
	DAT *p = head;//和下面n = 0相对应,head无数据
	DAT *q;
	int n = 0;
	while(p != NULL && n < leng)//找到尾节点,在尾节点的后一节点添加数据
	{
		p = p -> next;//循环第一次的时候p便指向了第一个节点,和n=1对应
		n++;//n = 1
	}//出循环时n=leng,p也就指向了表尾
	if(p == NULL || n > leng)//i过大(插入的位序的前一个如果还是空的话,那就超出了插入范围)或过小的时候报错,退出
	{
		printf("\n--位序错误,插入失败--\n");
		return head;
	}
        q = (DAT *)malloc(LEN);//新节点空间申请
	q -> data = e;//新节点赋值
	q -> next = p->next;//新节点定位(p -> next为第i个节点),将新节点与原表中第i个节点相连,即是替换了第i的位置
	//如果是表尾插入的话,q -> next == NULL
	p -> next = q;//再将新节点与前面的p节点相连,即完成了插入
	printf("\n--%d已添加到表尾--\n",e);
	return head;//返回头指针,方便打印链表
}
DAT *ListDelete(DAT *head,int i,int e)
{
	printf("\n请输入你要删除的位序:\n");
	scanf("%d",&i);
        DAT *p = head;
	DAT *q;
	int n = 0;
	while(p -> next != NULL && n < i - 1)//循环是为了找到要删除的节点的前驱,即p指向删除节点的上一个节点
	{
		p = p -> next;	
		n++;
	}
	if(p -> next == NULL || n > i -1)//i过大或过小的时候报错退出(要删除的节点不能为空)
	{
		printf("\n--位序错误,删除失败--\n");
		return head;
	}
	q = p -> next;//q指向p的下一节点
	p -> next = q -> next;//将p的下一节点绕到q的下一节点上去,完成对q节点的孤立,将q节点删除
	e = q -> data;//用e返回被删除的节点上的数据
	free(q);
	printf("\n--表中第%d位序上的数据:%d已被删除--\n",i,e);
	return head;
}
—————————单向链表主函数——————————
void main()
{
        DAT *L1,*L2;
	int i,e;
	int cur_e;
	int pre_e;
	int next_e;
	//开始测试函数
	L1 = InitList(L1);//创建空表
	L2 = InitList(L2);
	//L1 = ListInsert(L1,i,e);
	L1 = creatList(L1);//输入数据
	ListTraverse(L1);
	L2 = creatList(L2);
	ListTraverse(L2);
        //ListTraverse(L1);//输出数据
	ListLength(L1);//输出链表长度
	//ClearList(L1);//清空数据
	//ListTraverse(L1);//输出清空效果
        //destroyList(L1);
	//ListTraverse(L1);
	
	//查找位序上的元素数据
        /*printf("\n请输入你要查找的位序:\n");
	scanf("%d",&i);
	e = GetElem(L1,i,e);
	if(e != FALSE)
         printf("\n--位序%d上的数据为:%d--\n",i,e);*/
	//返回前驱
	//PirrorElem(L1,cur_e,pre_e);
	//返回后继
	//NextElem(L1,cur_e,next_e);
	//按位序插入新数据
	//L1 = ListInsert(L1,i,e);
	//按位序删除数据
	//L1 = ListDelete(L1,i,e);
	//表尾插入函数(拓展,方便进行合并链表的操作)
	/*printf("\n请输入要你在表尾插入的数据:\n");
	scanf("%d",&e);
	L1 = ListInsert_last(L1,e);
	ListTraverse(L1);*/
	//用自建函数合并两个不同数据的链表
        MergerLinks(L1,L2);//后面再做说明
}

三.单向链表基本函数的运用,合并链表(未排序)

void MergerLinks(DAT *L1,DAT *L2)
{
        DAT *L3 = NULL;
	DAT *p1 = NULL;
	DAT *p2 = NULL;
	DAT *p3 = NULL;
	DAT *p4 = NULL;
	DAT *p5 = NULL;
	int n = 0;
	int m = 0;
	int leng1;
	int leng2;
	int leng3;
	int i = 0,j = 0,k = 0;
	p1 = L1;
	p2 = L2;
	leng1 = ListLength(L1);
	leng2 = ListLength(L2);//链表长度获取
	L3 = (DAT *)malloc(sizeof(DAT));//第三链表的建立,用于存储表一和表二中不重复的数据
	p3 = p4 = (DAT *)malloc(sizeof(DAT));
	if(leng1 >= leng2)
	{
		for(i = 0;i < leng2;i++)
		{		
			if(p2 -> next != NULL)
			{p2 = p2 -> next;}
			
			for(j = 0; j < leng1;j++)
			{			
                         if(p1 -> next != NULL)
			{  
				p1 = p1 -> next;
					
			 }
			else//p1 -> next ==NULL的时候
			{
				p1 = L1 -> next;//重置p1回到L1的第一个节点
			}
			if(p2 -> data != p1 -> data)//表三数据链表的录入
			{	
                                 printf("\n--%d--\n",p2->data);
			         printf("\n-|%d|-\n",p1->data);
				 m++;
				 printf("\nm = %d\n",m);
				if(m == leng1)//将表二的一个数对比表一中所有的数,如果都不一样则将这个数插入表三
				{
				        p3 -> data = p2 -> data;
					if(p3 -> data != NULL)
					{
					        n++;
					        if(n == 1)
					           L3 -> next = p3;
                                                else
					           p4 -> next = p3;
					        p4 = p3;
					        p3 =(DAT*)malloc
                                                         (sizeof(DAT));   
					}
					p4 -> next = NULL;
					m = 0;//重置m
								
				}
			}
		}
		if(m <leng1)//m小于leng1说明有数重复,为了下一个数的比较可以顺利进行,标志m也要重置
		 m=0;
	}
        if(n==0)//结束查找,L3表中没有一个数据(n=0)
	L3 -> next = NULL; 
	if(L3 -> next != NULL)
	{    
	       printf("\n待插入的数据如下:\n");
	       ListTraverse(L3);
	       p5 = L3 -> next;
	       for(k = 0;k < n;k++)//n即为表三的长度
	        {
		       if(p5 -> data != NULL)
		       {
			      leng1 = leng1 +1;
			      L1 = ListInsert_last(L1,p5 -> data);//调用表尾插入函数
			 }
		         p5 = p5 -> next;
			 }
		}
		printf("\n--合并之后的链表如下:--\n");
                ListTraverse(L1);
}
	   
   else//leng1 < leng1的情况
   {
		for(i = 0;i < leng1;i++)
		{		
			if(p1 -> next != NULL)
			    {p1 = p1 -> next;}
			for(j = 0; j < leng2;j++)
			{
				
                              if(p2 -> next != NULL)
			            {  p2 = p2 -> next;}
                               else
				{p2 = L2 -> next;}
				if(p1 -> data != p2 -> data)//表三数据链表的录入
				{	
					m++;
					if(m == leng2)//将表二的一个数对比表一中所有的数,如果都不一样则将这个数插入表三
					{
					       p3 -> data = p1 -> data;
						if(p3 -> data != NULL)
						{
					        n++;
					        if(n == 1)
					          L3 -> next = p3;
                                                else
					           p4 -> next = p3;
					        p4 = p3;
					        p3 = (DAT*)malloc
                                                         (sizeof(DAT));
						}
						p4 -> next = NULL;
						m = 0;//重置m
					}
				}
			}
			if(m<leng2)
		        m=0;
		}
               if(n==0)
		L3 -> next = NULL;
		if(L3 -> next != NULL)
		{
		     ListTraverse(L3);
		     p5 = L3 -> next;
		     for(k = 0;k < n;k++)//n即为表三的长度
			 {
			     if(p5 -> data != NULL)
				 {
				     leng2 = leng2 +1;
				     L2 = ListInsert_last(L2,p5 ->                                                                                                                                       data);
				 }
			     p5 = p5 -> next;
			 }
		}
	   printf("\n--合并之后的链表如下:--\n");
       ListTraverse(L2);
   }
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值