【上机考试复习】数据结构头歌复习题(全部)

线性表

实现一个顺序存储的线性表

/*************************************************************
    date: April 2017
    copyright: Zhu En
    DO NOT distribute this code without my permission.
**************************************************************/
// 顺序表操作实现文件
//
#include <stdio.h>
#include <stdlib.h>
#include "Seqlist.h"
SeqList* SL_Create(int maxlen)
// 创建一个顺序表
// 与SqLst_Free()配对
{
    SeqList* slist=(SeqList*)malloc(sizeof(SeqList));
    slist->data = (T*)malloc(sizeof(T)*maxlen);
    slist->max=maxlen;
    slist->len=0;
    return slist;
}
void SL_Free(SeqList* slist)
// 释放/删除 顺序表
// 与SqLst_Create()配对
{
    free(slist->data);
    free(slist);
}
void SL_MakeEmpty(SeqList* slist)
// 置为空表
{
    slist->len=0;
}
int SL_Length(SeqList* slist)
// 获取长度
{
    return slist->len;
}
bool SL_IsEmpty(SeqList* slist)
// 判断顺序表是否空
{
    return 0==slist->len;
}
bool SL_IsFull(SeqList* slist)
// 判断顺序表是否满
{
    return slist->len==slist->max;
}
T SL_GetAt(SeqList* slist, int i)
// 获取顺序表slist的第i号结点数据
// 返回第i号结点的值
{
    if(i<0||i>=slist->len) 
    {
        printf("SL_GetAt(): location error when reading elements of the slist!\n");        
        SL_Free(slist);
        exit(0);
    }
    else 
    {
     	return slist->data[i];
    }
}
void SL_SetAt(SeqList* slist, int i, T x)
// 设置第i号结点的值(对第i号结点的数据进行写)
{
    if(i<0||i>=slist->len) 
    {
        printf("SL_SetAt(): location error when setting elements of the slist!\n");        
        SL_Free(slist);
        exit(0);
    }
    else 
    {
     	slist->data[i]=x;
    }
}
bool SL_InsAt(SeqList* slist, int i, T x)
// 在顺序表的位置i插入结点x, 插入d[i]之前
// i的有效范围[0,plist->len]
{
    if (i<0 || i>slist->len || slist->len==slist->max)  
    {
        printf("SL_InsAt(): location error, or slist full.\n");
        return false;
    }
    for (int j=slist->len; j>=i+1; j--) 
    {
        slist->data[j]=slist->data[j-1];
    }
    slist->data[i]=x;
    slist->len++;
    return true;
}
T SL_DelAt(SeqList* slist, int i)
// 删除顺序表plist的第i号结点
// i的有效范围应在[0,plist->len)内,否则会产生异常或错误。
// 返回被删除的数据元素的值。
{
    if (i<0 || i>=slist->len) 
    {
        printf("SL_DelAt(): location error!\n");
        SL_Free(slist);
        exit(0);
    }
    T res=slist->data[i];
    for (int j=i; j<slist->len-1; j++) 
    {
        slist->data[j] = slist->data[j+1];
    }
    slist->len--;
    return res;
}
int SL_FindValue(SeqList* slist, T x)
// 在顺序表表中查找第一个值为x的结点,返回结点的编号
// 返回值大于等于0时表示找到值为x的结点的编号,-1表示没有找到
{
    int i=0;
    while(i<slist->len && slist->data[i]!=x) i++;
    if (i<slist->len) return i;
    else return -1;
}
int SL_DelValue(SeqList* slist, T x)
// 删除第一个值为x的结点,
// 存在值为x的结点则返回结点编号, 未找到返回-1
{
 int i=SL_FindValue(slist, x);
    if (i>=0) 
    {
    	SL_DelAt(slist, i);
    }
    return i;
}
void SL_Print(SeqList* slist)
// 打印整个顺序表
{
    if (slist->len==0) 
    {
        printf("The slist is empty.\n");        
        return;
    }
    //printf("The slist contains: ");
    for (int i=0; i<slist->len; i++) 
    {
        printf("%d  ", slist->data[i]);
    }
    printf("\n");    
}

实现一个链接存储的线性表

/*************************************************************
    date: April 2017
    copyright: Zhu En
    DO NOT distribute this code without my permission.
**************************************************************/
// 单链表实现文件

#include <stdio.h>
#include <stdlib.h>
#include "LinkList.h"
LinkList* LL_Create()
// 创建一个链接存储的线性表,初始为空表,返回llist指针。
{
    LinkList* llist=(LinkList*)malloc(sizeof(LinkList));
    llist->front=NULL;
    llist->rear=NULL;
    llist->pre=NULL;
    llist->curr=NULL;
    llist->position=0;
    llist->len=0;
    return llist;
} 
void LL_Free(LinkList* llist)
// 释放链表的结点,然后释放llist所指向的结构。
{
    LinkNode* node=llist->front;
    LinkNode* nextnode;
    while(node)
    {
        nextnode=node->next;
        free(node);
        node=nextnode;
    }
    free(llist);
} 
void LL_MakeEmpty(LinkList* llist)
// 将当前线性表变为一个空表,因此需要释放所有结点。
{
    LinkNode* node=llist->front;
    LinkNode* nextnode;
    while(node)
    {
        nextnode=node->next;
        free(node);
        node=nextnode;
    }
    llist->front=NULL;
    llist->rear=NULL;
    llist->pre=NULL;
    llist->curr=NULL;
    llist->position=0;
    llist->len=0;
}   
int LL_Length(LinkList* llist)
// 返回线性表的当前长度。
{
    return llist->len;
}  
bool LL_IsEmpty(LinkList* llist)
// 若当前线性表是空表,则返回true,否则返回TRUE。
{
    return llist->len==0;
}
bool LL_SetPosition(LinkList* llist, int i)
// 设置线性表的当前位置为i号位置。
// 设置成功,则返回true,否则返回false(线性表为空,或i不在有效的返回)
// 假设线性表当前长度为len,那么i的有效范围为[0,len]
{    
    int k;
    /* 若链表为空,则返回*/
    if (llist->len==0) return false;
    /*若位置越界*/
    if( i < 0 || i > llist->len)
    {    printf("LL_SetPosition(): position error");
        return false;
    }
    /* 寻找对应结点*/
    llist->curr = llist->front;
    llist->pre = NULL;
    llist->position = 0;
    for ( k = 0; k < i; k++)    
    {
        llist->position++;
        llist->pre = llist->curr;
        llist->curr = (llist->curr)->next;
    }
    /* 返回当前结点位置*/
    return true;
}  
int LL_GetPosition(LinkList* llist)
// 获取线性表的当前位置结点的编号
{
    return llist->position;
}  
bool LL_NextPosition(LinkList* llist)
// 设置线性表的当前位置的下一个位置为当前位置。
// 设置成功,则返回true,否则返回false(线性表为空,或当前位置为表尾)
{
    if (llist->position >= 0 && llist->position < llist->len)
    /* 若当前结点存在,则将其后继结点设置为当前结点*/
    {
        llist->position++;
        llist->pre = llist->curr;
        llist->curr = llist->curr->next;
        return true;
    }
    else 
    {
        return false;
    }
}    
T LL_GetAt(LinkList* llist)
// 返回线性表的当前位置的数据元素的值。
{
    if(llist->curr==NULL)
    {
        printf("LL_GetAt(): Empty list, or End of the List.\n");
        LL_Free(llist);
        exit(1);
    }
    return llist->curr->data;
}   
void LL_SetAt(LinkList* llist, T x)
// 将线性表的当前位置的数据元素的值修改为x。
{
    if(llist->curr==NULL)
    {
        printf("LL_SetAt(): Empty list, or End of the List.\n");
        LL_Free(llist);
        exit(1);
    }
    llist->curr->data=x;
}    
bool LL_InsAt(LinkList* llist, T x)
// 在线性表的当前位置之前插入数据元素x。当前位置指针指向新数据元素结点。
// 若插入失败,返回false,否则返回true。
{    
    LinkNode *newNode=(LinkNode*)malloc(sizeof(LinkNode));
    if (newNode==NULL) 
    {
    	return false;
    }
    newNode->data=x;
    if (llist->len==0)
    {
        /* 在空表中插入*/
        newNode->next=NULL;
        llist->front = llist->rear = newNode;
    }
    //当前位置为表头
    else if (llist->pre==NULL)
    {
        /* 在表头结点处插入*/
        newNode->next = llist->front;
        llist->front = newNode;
    }
    else 
    {  
        /* 在链表的中间位置或表尾后的位置插入*/
        newNode->next = llist->curr;
        llist->pre->next=newNode;
    }
    //插入在表尾后
    if (llist->pre==llist->rear)
        llist->rear=newNode;
    /* 增加链表的大小*/
    llist->len++;
    /* 新插入的结点为当前结点*/
    llist->curr = newNode;
    return true;
}    
bool LL_InsAfter(LinkList* llist, T x)
// 在线性表的当前位置之后插入数据元素x。空表允许插入。当前位置指针将指向新结点。
// 若插入失败,返回false,否则返回true。
{
    LinkNode *newNode=(LinkNode*)malloc(sizeof(LinkNode));
    if (newNode==NULL) return false;
    newNode->data=x;
    if (llist->len==0)    
    {
        /* 在空表中插入*/
        newNode->next=NULL;
        llist->front = llist->rear = newNode;
    }
    else if (llist->curr == llist->rear || llist->curr == NULL)    {
        /* 在尾结点后插入*/
        newNode->next = NULL;
        llist->rear->next=newNode;
        llist->pre=llist->rear;
        llist->rear=newNode;
        llist->position=llist->len;
    }
    else
    {
        /* 在中间位置插入*/
        newNode->next = llist->curr->next;
        llist->curr->next=newNode;
        llist->pre=llist->curr;
        llist->position ++;
    }
    /* 增加链表的大小*/
    llist->len ++;
    /* 新插入的结点为当前结点*/
    llist->curr = newNode;
    return true;
}
bool LL_DelAt(LinkList* llist)
// 删除线性表的当前位置的数据元素结点。
// 若删除失败(为空表,或当前位置为尾结点之后),则返回false,否则返回true。
{    
    LinkNode *oldNode;
    /* 若表为空或已到表尾之后,则给出错误提示并返回*/
    if (llist->curr==NULL)
    {    
        printf("LL_DelAt(): delete a node that does not exist.\n");
        return false;
    }
    oldNode=llist->curr;
    /* 删除的是表头结点*/
    if (llist->pre==NULL)
    {    
        llist->front = oldNode->next;
    }
    /* 删除的是表中或表尾结点*/
    else if(llist->curr!=NULL)
    {
        llist->pre->next = oldNode->next;
    }
    if (oldNode == llist->rear)    
    {
        /* 删除的是表尾结点,则修改表尾指针和当前结点位置值*/
        llist->rear = llist->pre;
    }
    /* 后继结点作为新的当前结点*/
    llist->curr = oldNode->next;
    /* 释放原当前结点*/
    free(oldNode);
    /* 链表大小减*/
    llist->len --;
    return true;
}    
bool LL_DelAfter(LinkList* llist)
// 删除线性表的当前位置的后面那个数据元素。
// 若删除失败(为空表,或当前位置时表尾),则返回false,否则返回true。
{
    LinkNode *oldNode;
    /* 若表为空或已到表尾,则给出错误提示并返回*/
    if (llist->curr==NULL || llist->curr== llist->rear)
    {
        printf("LL_DelAfter():  delete a node that does not exist.\n");
        return false;
    }
    /* 保存被删除结点的指针并从链表中删除该结点*/
    oldNode = llist->curr->next;
    llist->curr->next=oldNode->next;
    if (oldNode == llist->rear)
    {
     /* 删除的是表尾结点*/
        llist->rear = llist->curr;
    }
    /* 释放被删除结点*/
    free(oldNode);
    /* 链表大小减*/
    llist->len --;
    return true;
}    
int LL_FindValue(LinkList* llist, T x)
// 找到线性表中第一个值为x的数据元素的编号。
// 返回值-1表示没有找到,返回值>=0表示编号。
{
    LinkNode* p=llist->front;
    int idx=0;
    while(p!=NULL && p->data!=x) 
    {
        idx++;
        p = p->next;
    }
    if (idx>=llist->len) 
    {
  	return -1; 
    }
    else 
    {
  	return idx; 
    }
}
int LL_DelValue(LinkList* llist, T x)
// 删除第一个值为x的数据元素,返回该数据元素的编号。如果不存在值为x的数据元素,则返回-1.
{
    int idx=LL_FindValue(llist, x);
    if (idx<0) return -1;
    LL_SetPosition(llist, idx);
    LL_DelAt(llist);
    return idx;
}   
void LL_Print(LinkList* llist)
// 打印整个线性表。
{
    LinkNode* node=llist->front;
    while (node) 
    {
        printf("%d ", node->data);
        node=node->next;
    }
    printf("\n");
}

函数

第1关 求和

#include<stdio.h>
//编写函数
/*********Begin*********/
int sum1(int n){
    int sum = 0;
    for(int i = 1; i <= n; i++){
        sum += i;
    }
    return sum;
}
/*********End**********/ 
int main(void)
{  
    /*********Begin*********/
    int n = 0;
    scanf("%d", &n);
    int sum = sum1(n);
    printf("%d", sum);

    /*********End**********/ 
    return 0;
}

第2关 回文数计算

#include<stdio.h>
void solve(){
    /*********Begin*********/
    for(int i=200; i <= 999; i++){
    	int a = i/100; // 百位数字
		int b = i%100%10; // 个位数字 
		if(a == b){
			printf("%d\n",i);
		}
	}
	for(int i=1000; i <= 3000; i++){
    	int a = i/1000; // 千位数字
		int b = i/100%10; // 百位数字 
		int c = i/10%100%10; // 十位数字 
		int d = i%1000%100%10; // 个位数字 
		if(a == d && b == c){
			printf("%d\n",i);
		}
	}
	

    /*********End**********/ 
}
int main(void)
{  
    solve();
   return 0;
}

第3关 编写函数求表达式的值

#include<stdio.h>
//编写题目要求的函数
/*********Begin*********/
double sum1(int n){
	double sum = 1;
	if(n==1) 
	    return 1;
	else {
	for(int i = n+1; i >= 2; i--){ // 为啥是n+1呢??因为下面这个公式是用n推导的 
		sum = 1 + (double)(((i-1)*sum)/(2*i-1));
	}	
	return sum;
	}
}

/*********End**********/ 
int main(void)
{  
    /*********Begin*********/
// 1+1/3(1+2/5(1+......(1+(n-1)/(2n-1))))
    int n=0;
	double sum=0;
    scanf("%d", &n);
    sum = sum1(n);
    printf("%.10lf", sum);

    /*********End**********/ 
    return 0;
}

第4关 阶乘数列


#include<stdio.h>
//编写函数
/*********Begin*********/
void sum1(int n){
	long long int sum = 1;
	if(n==1) 
	    printf("1");
	else {
	for(int i = n; i >= 2; i--){ 
		sum = 1 + i*sum;
	}	
	printf("%lld",sum);
	}
}
/*********End**********/ 
int main(void)
{  
    /*********Begin*********/
    int n=0;
    scanf("%d", &n);
    sum1(n);

    /*********End**********/ 
    return 0;
}


第5关 亲密数

#include<stdio.h>
// 求因子的函数 
int sum(int n){ 
	int sum = 0;
	for(int i = 1; i <= n/2; i++){
		if(n%i == 0){
			sum += i;
		}
	}
	return sum;
}
void solve(){
    /*********Begin*********/
    for(int i = 1; i <= 3000; i++){ // i是第一个数字 
    	int b = sum(i); // b是i的因子和 
    	int c = sum(b); // c是b的因子和(c与i相同√) 
    	// c==i:确定是亲密数 
		// b!=i且b<i:不会有6 6 这种出现同时输出结果不会是bi的反向输出 
    	if(c == i && b != i && b < i){
    		printf("(%d,%d)", b,i);
		}

	}

    /*********End**********/ 
}
int main(void)
{   
    solve();
    return 0;
}

第6关 公约公倍数

#include<stdio.h>
//编写最大公约数GCD函数
/*********Begin*********/
 long long int  GCD(long long int a, long long int b){
	long long int max = 1;
	for(long long int i = 1; i <= a; i++){ 
		if(a%i == 0 && b%i == 0){
			max = i;
		}
	}
	return max;
	
}
/*********End**********/ 

//编写最小公倍数LCM函数
/*********Begin*********/
int LCM(int a, int b){
	
}
/*********End**********/ 
int main(void)
{  
    long long int a,b;
    /*********Begin*********/
    scanf("%lld %lld",&a,&b);
    if(a>b){
    	long long int temp = a;
    	a = b;
    	b = temp;  	
	} // a一直小于b
	if(a<0 || b <0){
		printf("Input Error");
	}else {	
	long long int max = GCD(a,b);
	long long int min = a * b / max;
	printf("%lld %lld",max,min);
    } 
    /*********End**********/ 
    return 0;
}

一维数组和二维数组

第1关 排序问题

#include<stdio.h>
int main(void)
{
    /*********Begin*********/
    int a[10];
    for(int i = 0; i < 10; i++){
    	scanf("%d", &a[i]);
    }
    for(int i = 0; i < 10; i++){
    	for(int j = i; j < 10; j++){
    		if(a[i] < a[j]){
    			int temp = a[i];
    			a[i] = a[j];
    			a[j] = temp;
    		}
    	}
    }
    for(int i = 0; i < 10; i++){
    	printf("%d ", a[i]);
    }
    /*********End**********/
    return 0;
}

第2关 查找整数

#include<stdio.h>
int main(void)
{
    /*********Begin*********/
    int n = 0;
    scanf("%d", &n);
    int a[n];
    for(int i = 0; i < n; i++){
    	scanf("%d", &a[i]);
    }
    int index = 0;
    int temp = 0;
    scanf("%d", &index);
    for(int i = 0; i < n; i++){
    	if(a[i] == index){
    		printf("%d", i+1);
    		temp++;
    		break;
    	} 	
    }
    if(temp == 0){
    	printf("-1");
    }
    /*********End**********/
    return 0;
}

第3关 计算数组中元素的最大值及其所在的行列下标值

/*
题目描述:按如下函数原型编程从键盘输入一个m行n列的二维数组,
然后计算数组中元素的最大值及其所在的行列下标值。其
中m和n的值由用户键盘输入。
已知m和n的值都不超过10。

相关知识(略)
输入
输入数组大小:"%d,%d"
下面输入数组中元素。

输出
输出格式:
数组大小输入提示信息:"Input m, n:"
数组元素输入提示信息:"Input %d*%d array: "
输出格式:"max=%d, row=%d, col=%d"

样例输入
5,5
1 2 3 4 5
4 5 6 100 2
3 2 1 5 6
1 2 3 5 4
3 5 6 4 8

样例输出
Input m, n:Input 5*5 array:
max=100, row=2, col=4
*/
#include<stdio.h>
int main(void)
{
    /*********Begin*********/
    int m, n;
    scanf("%d,%d", &m, &n);
    int a[m][n];
    for(int i = 0; i < m; i++){
    	for(int j = 0; j < n; j++){
    		scanf("%d", &a[i][j]);
		}
	}
	int max = a[0][0];
	int row=0, col=0;
	for(int i = 0; i < m; i++){
    	for(int j = 0; j < n; j++){
    		if(max <= a[i][j]){
    			max = a[i][j];
    			row = i+1;
    			col = j+1;
			}
		}
	}
	printf("Input m, n:Input %d*%d array:\n", m,n);
    printf("max=%d, row=%d, col=%d", max,row,col);
	


    /*********End**********/
    return 0;
}

第4关 二分查找

# include<stdio.h>
# define m 1000
int main()
{
int a[m],n,b;
    scanf("%d",&b);
    for(n=0;n<b;n++){
        scanf("%d",&a[n]);
    }
    int find;
    scanf("%d",&find);
  if(b==1){
    if(find==a[0]){
      printf("1");
    }

  }
    for(n=0;n<(b/2);n++) {
        if (find == a[n]) {
            printf("%d", n + 1);
            break;
        }
    }
if(find!=a[n]) {
    for (n = (b / 2); n < b; n++) {
        if (find == a[n]) {
            printf("%d", n + 1);
            break;
        }
    }
}
if(find!=a[n]){
  printf("None");
}
return 0;
}

第5关 鞍点

#define N 10
#include <stdio.h>
int Maxcol(int a[][N],int n,int row){
    int i,maxcol=0;
    for(i=1;i<n;i++)
        if (a[row][i]>a[row][maxcol]) maxcol=i;
    return maxcol;
}
int Minrow(int a[][N],int m,int col){
    int i,minrow=0;
    for(i=1;i<m;i++)
        if (a[i][col]<a[minrow][col]) minrow=i;
    return minrow;
}
int main(){
    int m,n,i,j;
    int maxcol,minrow;
    int a[N][N];
    scanf("%d %d",&m,&n);
    for(i=0;i<m;i++) for(j=0;j<n;j++)
        scanf("%d",&a[i][j]);
    for(i=0;i<m;i++){
        maxcol=Maxcol(a,n,i);
        minrow=Minrow(a,m,maxcol);
        if (i==minrow){
            printf("Array[%d][%d]=%d\n",i,maxcol,a[i][maxcol]);
            break;
        }
    }
    if(i>=m) printf("None\n");
}

第6关 删除最大值

#include<stdio.h>
int main(void)
{
	int a[10];
	int b[9];
	for(int i = 0; i < 10; i++){
		scanf("%d", &a[i]);
	}
	int max = 0;
	for(int i = 0; i < 10; i++){
		if(a[i] > max){
			max = a[i];
		}
	}
	int n = 0;
	for(int i = 0; i < 10; i++){
		if(a[i] != max){
			b[n] = a[i];
			printf("%d ", b[n]);
			n++;
		}
	}
  
    return 0;
}

指针

第1关 用指针法输入12个整数,然后按每行4个数输出

#include<stdio.h>
int main(void)
{
	int a[12];
	int *p = a;
	for(int i = 0; i < 12; i++){
		scanf("%d", p++);
	}
	p = a;
	for(int i = 0; i < 12; i++){
		if((i+1)%4==0){
				printf("%d", *(p+i));
				printf("\n");
		}
		else printf("%d ", *(p+i));
	}
    
    return 0;
}

第2关 指针变量作为函数参数实现两变量交换值

#include<stdio.h>
/*********Begin*********/
void exchange(int *a,int *b){
	printf("%d %d",*b, *a);
}

/*********End**********/
int main(void)
{
	int a,b;
	scanf("%d%d",&a,&b);
	/*********Begin*********/
	exchange(&a,&b);


	/*********End**********/
    return 0;
}

第3关 报数

#include<stdio.h>
int main()
{
    int n,a[1000];
    int i,j=1,k=0,m=1;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
       a[i]=i;
    while(k<n-1)
    {
        for(i=1;i<=n;i++)
        {
            
           for(j=m;j<=3;)
            {
                 if(a[i]!=0)
                 {
                    a[i]=j;    
                   m++;    
                   if(m==4)
                   {
                     m=1;  
                   }
                 }
                break; 
            }
             
           if(a[i]==3)
           {
             a[i]=0;
             k++;
           }               
        }       
    }
    for(i=1;i<=n;i++)
    {
        if(a[i]!=0)
          printf("%d",i);
    }
}

第4关 strcmp函数

#include<stdio.h>
int strcmp(char *p1, char *p2)
{
    int t;
    for (; *p1!='\0'||*p2!='\0'; p1++, p2++)
        if (*p1!=*p2){
            t=*p1-*p2;
            break;
        }
    if (*p1=='\0'&&*p2=='\0')
        t=0;
    return t;
}
int main(void)
{
	char a[110],b[110];
	scanf("%s%s",a,b);
	if(strcmp(a,b)>0)
		printf("%s", a);
	else
		printf("%s", b);


    return 0;
}

第1关 实现图的宽度优先遍历

//Graph
///
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Graph.h"
/

Graph* Graph_Create(int n)
{
	Graph* g=(Graph*)malloc(sizeof(Graph));
	g->n=n;
	g->vetex=(char**)malloc(sizeof(char*)*n);
	int i;
	for (i=0; i<n; i++) g->vetex[i] = NULL;
	g->adj=(int*)malloc(sizeof(int)*n*n);
	int j;
	for(i=0; i<n; i++) {
		for(j=0; j<n; j++) {
			g->adj[i*n+j]=0;
		}
	}
	return g;
}

void Graph_Free(Graph* g)
{
	free(g->adj);
	int i;
	for (i=0; i<g->n; i++) free(g->vetex[i]);
	free(g->vetex);
	free(g);
}

int Graph_WidthFirst(Graph*g, int start, Edge* tree)
//从start号顶点出发宽度优先遍历,(编号从0开始)
//返回访问到的顶点数,
//tree[]输出遍历树
//返回的tree[0]是(-1, start), 
//真正的遍历树保存在tree[1..return-1], return是返回值
//顶点的访问次序依次为tree[0].to, tree[1].to,  ..., tree[return-1].to
//输入时,tree[]的长度至少为顶点数
//返回值是从start出发访问到的顶点数
{
	const int MAX=1000;
	Edge queue[MAX];
	int head=0, tail=0;
#define In__(a,b)  {queue[tail].from=a; queue[tail].to=b; tail=(tail+1)%MAX;}/
#define Out__(a,b)  {a=queue[head].from; b=queue[head].to; head=(head+1)%MAX;}//
#define QueueNotEmpty (head!=tail?1:0)///
#define HasEdge(i,j)  (g->adj[(i)*g->n+(j)]==1)

	char* visited=(char*)malloc(sizeof(char)*g->n);
	memset(visited, 0, sizeof(char)*g->n);

	int parent=-1;  
	int curr=start;
	In__(parent, curr); 
	int k=0; //已经访问的结点数
	/*请在BEGIN和END之间实现你的代码*/
    /*****BEGIN*****/
     while (QueueNotEmpty) {
        Out__(parent, curr); 
        if (visited[curr]) continue;
        visited[curr]=1;
        tree[k].from=parent; tree[k].to=curr; k++;
        int j;
        for (j=0; j<=g->n-1;j++) {
            if (HasEdge(curr,j) && !visited[j])In__(curr,j);
        }
    }
    /*****END*******/
	return k;
#undef In__//
#undef Out__///
#undef QueueNotEmpty
#undef HasEdge
}

第2关 实现图的深度优先遍历

//Graph
///
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Graph.h"
/
Graph* Graph_Create(int n)
{
	Graph* g=(Graph*)malloc(sizeof(Graph));
	g->n=n;
	g->vetex=(char**)malloc(sizeof(char*)*n);
	int i;
	for (i=0; i<n; i++) g->vetex[i] = NULL;
	g->adj=(int*)malloc(sizeof(int)*n*n);
	int j;
	for(i=0; i<n; i++) {
		for(j=0; j<n; j++) {
			g->adj[i*n+j]=0;
		}
	}
	return g;
}

void Graph_Free(Graph* g)
{
	free(g->adj);
	int i;
	for (i=0; i<g->n; i++) free(g->vetex[i]);
	free(g->vetex);
	free(g);
}

int Graph_DepthFirst(Graph*g, int start, Edge* tree)
//从start号顶点出发深度优先遍历,(编号从开始)
//返回访问到的顶点数,
//tree[]输出遍历树
//返回的tree[0]是(-1, start), 
//真正的遍历树保存在tree[1..return-1], return是返回值
//顶点的访问次序依次为tree[0].to, tree[1].to, ..., tree[return-1].to
//输入时,tree[]的长度至少为顶点数
//返回值是从start出发访问到的顶点数
{
	/*请在BEGIN和END之间实现你的代码*/
    /*****BEGIN*****/
    /*请在BEGIN和END之间实现你的代码*/
/*****BEGIN*****/
const int MAX=1000;
    Edge stack[MAX];
    int top=-1;
#define Push__(a,b)  {top++; stack[top].from=a; stack[top].to=b;}
#define Pop__(a,b)  {a=stack[top].from; b=stack[top].to; top--;}///
#define StakNotEmpty (top>=0?1:0)
#define HasEdge(i,j)  (g->adj[(i)*g->n+(j)]==1)
    char* visited=(char*)malloc(sizeof(char)*g->n);
    memset(visited, 0, sizeof(char)*g->n);
    int parent=-1;  
    int curr=start;
    Push__(parent, curr); 
    int k=0; //已经访问的结点数
    while (StakNotEmpty) {
        Pop__(parent, curr); 
        if (visited[curr]) continue;
        visited[curr]=1;
        tree[k].from=parent; tree[k].to=curr; k++;
        int j;
        for (j=g->n-1; j>=0; j--) {
            if (HasEdge(curr,j) && !visited[j]) Push__(curr,j);
        }
    }
    free(visited);
    return k;
#undef Push__
#undef Pop__///
#undef StakNotEmpty//
#undef HasEdge
/*****END*******/
    /*****END*******/
}

第1关 由双遍历序列构造二叉树

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "ConstructTree.h"



 

/*

InPreToTree(): 由前序遍历序列和中序遍历序列构造二叉树

前序序列为pa[p1:p2]

中序序列为ia[i1:i2]

返回所构造的二叉树的根指针

*/

TNode* InPreToTree(char *pa, char *ia, int p1, int p2, int i1, int i2)

{
    /*请在BEGIN和END之间实现你的代码*/

    /*****BEGIN*****/

    TNode *root=new TNode;

    root->data=pa[p1];

    root->left=NULL;

    root->right=NULL;                  //递归终止条件:先序遍历中只有一个结点

    if(pa[p1]==pa[p2])

    return root;

                                      //数组元素名是其首地址

    int a=i1;

    while(ia[a]!=root->data&&a<=i2)    //中序里面

    a++;

    if(ia[a]!=root->data)             //数组2遍历完成

        exit(0);

    int leftlen=a-i1;  

    if(leftlen>0)                   //左子树的个数

    root->left=InPreToTree(pa,ia,p1+1,p1+leftlen,i1,a-1);

    

    int rightlen=i2-a;

    if(rightlen>0)

    root->right=InPreToTree(pa,ia,p2-rightlen+1,p2,a+1,i2);

 return root;

    

    /******END******/

    /*请不要修改[BEGIN,END]区域外的代码*/

} 

void PrintPostTravel(TNode* t)

{
    if(t==NULL) return;

    if(t->left) PrintPostTravel(t->left);

    if(t->right) PrintPostTravel(t->right);

    printf("%c", t->data);

}

void DeleteTree(TNode* t)

{
    if(t==NULL) return;

    if(t->left) DeleteTree(t->left);

    if(t->right) DeleteTree(t->right);

    delete t;

}

第2关 打印二叉树

///
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "PrintTree.h"
/

/*=================================================
函数:TNode* LayersToTree(char *A, int n) 
功能:由补充虚结点(‘^’)的层序序列构造二叉树
参数:A: 补充了虚结点(‘^’)的层序序列
          n: 序列的长度
输出: 所构造的二叉树的根指针,NULL表示没有构造出二叉树
==================================================*/
TNode* LayersToTree(char *A, int n)
{
	TNode**  pnode;
	TNode* node;
	int i;
	if(n<=0)
		return NULL;
	pnode= new TNode*[n];
	for(i=0; i<n; i++){
		if(A[i]!='^'){
			node=new TNode;
			node->data=A[i];
			node->left=NULL;
			node->right=NULL;
			pnode[i]=node;
		}
		else 
			pnode[i]=NULL;
	}
	for(i=0; i<n; i++){
		if(pnode[i]==NULL) continue;
		if(2*(i+1)<=n && pnode[2*(i+1)-1]!=NULL)
			pnode[i]->left=pnode[2*(i+1)-1];
		if(2*(i+1)+1<=n && pnode[2*(i+1)]!=NULL)
			pnode[i]->right=pnode[2*(i+1)];
	}
	node=pnode[0];
	delete pnode;
	return node;
} 

void PrintTreeRootLeft(TNode* r, int layer)
//r是树中一棵子树的根,打印以结点r为根的子树,
//layer是r在树中所处的层,约定树的根结点的层号为1
{
    /*请在BEGIN和END之间实现你的代码*/
    /*****BEGIN*****/
 const int W=5;
    int i,j;
    if (r==NULL) return;
    PrintTreeRootLeft(r->right, layer+1);
    for(i=1; i<layer; i++) {
        for (j=0;j<W;j++) printf("%c", '-');
    }
    for (j=0; j<W-1; j++) printf("%c",'-');
    printf("%c\n", r->data);
    PrintTreeRootLeft(r->left, layer+1);
    /******END******/
    /*请不要修改[BEGIN,END]区域外的代码*/
}

void DeleteTree(TNode* t)
{
	if(t==NULL) return;
	if(t->left) DeleteTree(t->left);
	if(t->right) DeleteTree(t->right);
	delete t;
}

计算表达式

第1关 栈的应用 - 计算中缀表达式

/**********************************************************
	date: July 2017
    copyright: Zhu En(祝恩)
    DO NOT distribute this code.
**********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "LnkStack.h"
#include "Infix.h"

//
void compute(LinkStack* so, LinkStack* sd)
//++++++++++++++++++++++++++++++++++++++++++++++
//so 运算符栈
//sd 操作数栈
//1 从运算符栈出栈一个运算符
//2 从操作数栈出栈两个操作数
//3 用出栈的运算符对出栈的操作数进行运算
//4 将运算结果进操作数栈
//+++++++++++++++++++++++++++++++++++++++++++++++
{
	T a,b,c,d;
	LS_Pop(so,c);
	LS_Pop(sd,a);
	LS_Pop(sd,b);
	if (c=='*') d=b*a;
	else if (c=='/') d=b/a;
	else if (c=='+') d=b+a;
	else if (c=='-') d=b-a;
	else printf("never occur!");
	LS_Push(sd, d);
}

double ComputeInfix(char* s)
{
    // 请在此添加代码,补全函数ComputeInfix,计算中缀表达式
    /********** Begin *********/
    int i=0;
    LinkStack* so=LS_Create(); 
    LinkStack* sd=LS_Create(); 
    while(s[i]) {
        if ('0'<=s[i] && s[i]<='9') {
            LS_Push(sd, s[i++]-48);
            continue;
        }
        if(s[i]=='('||LS_IsEmpty(so)) {
            LS_Push(so, s[i++]); 
            continue;
        }
        if(s[i]==')') {
            T topitem;
            while(LS_Top(so,topitem) && topitem !='(' ) 
                compute(so, sd);
            LS_Pop(so,topitem);
            i++;
            continue;
        }
        if(s[i]=='*'||s[i]=='/') {
            T c;
            LS_Top(so,c);
            if (c=='*' || c=='/') 
                compute(so, sd);
            LS_Push(so, s[i++]);
            continue;
        }
        if(s[i]=='+'||s[i]=='-') {
            T topitem;
            while(LS_Top(so,topitem) && topitem !='(' ) 
                compute(so, sd);
            LS_Push(so, s[i++]);
            continue;
        }
    }
    while(!LS_IsEmpty(so)) 
        compute(so, sd);
    T res;
    LS_Top(sd,res);
    LS_Free(so);
    LS_Free(sd);
    return res;
    /********** End **********/
}

第2关 栈的应用 - 计算后缀表达式

/**********************************************************
	date: July 2017
    copyright: Zhu En
    DO NOT distribute this code.
**********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "LnkStack.h"
#include "Postfix.h"

double ComputePostfix(char* s)
{
    // 请在此添加代码,补全函数ComputePostfix,计算后缀表达式
    /********** Begin *********/
    LinkStack* sd=LS_Create();
    int i=0;
    T k,top1,top2;
    while(s[i]) {
        switch (s[i]) {
        case '+':
            LS_Pop(sd,top1);
            LS_Pop(sd,top2);
            k=top1+top2;
            LS_Push(sd,k);
            break;
        case '-':
            LS_Pop(sd,top1);
            LS_Pop(sd,top2);
            k=top2-top1;
            LS_Push(sd,k);
            break;
        case '*':
            LS_Pop(sd,top1);
            LS_Pop(sd,top2);
            k=top1*top2;
            LS_Push(sd,k);
            break;
        case '/':
            LS_Pop(sd,top1);
            LS_Pop(sd,top2);
            k=top2/top1;
            LS_Push(sd,k);
            break;
        default:
            LS_Push(sd, (int)(s[i]-48));
        }
        i++;
    }
    T res;
    LS_Top(sd,res);
    LS_Free(sd);
    return res;
    /********** End **********/
}

字符串匹配

第1关 实现朴素的字符串匹配

int FindSubStr(char* t, char* p)
/*
从字符串t查找子字符串p。
字符串以数值结尾,例如p="str",那么p[0]='s',p[1]='t',p[2]='r',p[3]=0。
采用朴素的匹配算法,返回子字符串第一次出现的位置,例如t="string ring",p="ring",则返回2。
若没有找到,则返回-1。
*/
{
    // 请在此添加代码,补全函数FindSubStr
    /********** Begin *********/
    int i=0, j=0;
    while(p[i]!=0 && t[j]!=0) {
        if (p[i]==t[j]) {
            i ++;
            j ++;
        }
        else {
            j = j-i+1;
            i = 0;
        }
    }
    if (p[i] == 0) return j-i;
    else return -1;
    /********** End **********/
}

第2关 实现KMP字符串匹配

/*************************************************************
    date: 
    copyright: Zhu En
    DO NOT distribute this code without my permission.
**************************************************************/
//字符串 实现文件
//
#include <stdio.h>
#include <stdlib.h>
#include "kmp.h"
/

void KmpGenNext(char* p, int* next)
//生成p的next数组, next数组长度大于等于字符串p的长度加1
{
    next[0]= -1;
    int k= -1;
    for (int i=1; p[i-1]!=0; i++) 
    {    
        while(k>=0&&p[k]!=p[i-1])   k=next[k];
        k=k+1;
        if (p[i]==p[k])  next[i]=next[k];
        else    next[i]=k;
    }
}


int KmpFindSubWithNext(char* t, char* p, int* next)
//从t中查找子串p的第一次出现的位置
//若找到,返回出现的位置,否则返回-1
{
	int i=0, j=0;
	while(p[i]!=0 && t[j]!=0)	{
		if(p[i]==t[j]) 	{ 
			i++;  
			j++; 
		}
		else  if (next[i]>=0) {
			i = next[i];
		}
		else  { 
			i=0;  
			j++; 
		}
	}
	if(p[i]==0)  return j-i; //found
	else  return -1;  //not found
}


队列

第1关 实现一个顺序存储的队列

#include <stdio.h>
#include <stdlib.h>
#include "SeqQueue.h"
SeqQueue* SQ_Create(int maxlen)
// 创建顺序队列, 队列最多存储maxlen个队列元素
{
    SeqQueue* sq=(SeqQueue*)malloc(sizeof(SeqQueue));
    sq->data=(T*)malloc(sizeof(T)*(maxlen+1));
    sq->front=sq->rear=0;
    sq->max=maxlen+1;
    return sq;
}
void SQ_Free(SeqQueue* sq)
// 释放队列空间,以删除队列
{
    free(sq->data);
    free(sq);
}
void SQ_MakeEmpty(SeqQueue* sq)
// 将队列置空
{
    sq->front=0;
    sq->rear=0;
}
bool SQ_IsEmpty(SeqQueue* sq)
// 判断队列是否为空,为空返回true,否则返回false。
{
    return sq->front==sq->rear;
}
bool SQ_IsFull(SeqQueue* sq)
// 判断队列是否为满。为满返回true,否则返回false。
{
    return (sq->rear+1)%sq->max==sq->front;
}
int SQ_Length(SeqQueue* sq)
// 队列长度
{
    return (sq->rear-sq->front+sq->max)%sq->max;
}
bool SQ_In(SeqQueue* sq, T x)
// 将x入队。若入队失败(队列满),则返回false,否则返回true。
{
    if(SQ_IsFull(sq)) 
 {
  return false; 
 }
    else
 {
        T* head=sq->data;
        head[sq->rear]=x;
        sq->rear=(sq->rear+1)%sq->max;
        return true;
    }
}
bool SQ_Out(SeqQueue* sq, T& item)
// 从队列sq出队一个元素,返回时item为出队的元素的值。若出队成功(队列不为空),则返回true,否则(队列空),返回false,此时item不会返回有效值。
{
    if(SQ_IsEmpty(sq)) 
 {
  return false;
 }
    else
 {
        T* head=sq->data;
        item=head[sq->front];
        sq->front=(sq->front+1)%sq->max;//记得加sq->不要疏忽大意
        return true;
    }
}
bool SQ_Head(SeqQueue* sq, T& head)
// 获取队列头结点元素,返回时head保存头结点元素。
// 若获取失败(队列空),则返回值为false,否则返回值为true。
{
    if ( SQ_IsEmpty(sq) )
 {
        return false;
    }
    else 
 {
        head = sq->data[sq->front];
        return true;
    }
}
void SQ_Print(SeqQueue* sq)
// 依次打印出队列中的每个元素
{
    int i=sq->front;
    if (SQ_IsEmpty(sq)) 
 {
        printf("queue is emtpy");
        return;
    }
    for (i=sq->front; i!=sq->rear; i=(i+1)%sq->max) 
 {
        printf("%d  ", sq->data[i]);
    }
    printf("\n");
}

第2关 实现一个链接存储的队列

#include <stdio.h>
#include <stdlib.h>
#include "CLnkQueue.h"

LNode* CLQ_Create()
// 创建一个队列
{
    LNode* rear=(LNode*)malloc(sizeof(LNode));
    rear->data = 0;
    rear->next = rear;
    return rear;
}
void CLQ_Free(LNode* rear)
// rear指向尾结点
{
    CLQ_MakeEmpty(rear);
    free(rear);
}

void CLQ_MakeEmpty(LNode* & rear)
// rear指向尾结点
// 将队列变为空队列
{
    T item;
    while(!CLQ_IsEmpty(rear))
        CLQ_Out(rear,item);
}

bool CLQ_IsEmpty(LNode* rear)
// 判断队列是否为空
{
    // 请在这里补充代码,完成本关任务
    
    return rear->next->data==0;
	//return rear->next->next==rear->next;
    
}

int CLQ_Length(LNode* rear)
// 返回队列长度,rear指向尾结点
{
    // 请在这里补充代码,完成本关任务
   
    return rear->next->data;
   
}

void CLQ_In(LNode* & rear, T x)
// 入队列, 新结点加入链表尾部。rear指向尾结点
{
    // 请在这里补充代码,完成本关任务
   
	LNode*node=(LNode*)malloc(sizeof(LNode));
    node->data=x;
    node->next=rear->next;
    rear->next=node;
    rear=node;
    rear->next->data++;
    
}

bool CLQ_Out(LNode* & rear, T& item)
// 出队列。空队列时,返回值为false。rear指向尾结点
{
    if(CLQ_IsEmpty(rear)){
    	return false;
	}
     if(rear->next->next==rear){
    	LNode*node=rear;
    	item=rear->data;
    	rear->next->next=rear->next;
    	rear=rear->next;
    	free(node);
	}else{
		item=rear->next->next->data;
		LNode*node=rear->next->next;
		rear->next->next=node->next;
		free(node);
		
	}
	rear->next->data--;
    return true;
    
}

bool CLQ_Head(LNode* rear, T& item)
// rear指向尾结点
// 获取队列头。空队列时返回值为false。
{
    if (CLQ_IsEmpty(rear)) 
        return false;

    item = rear->next->next->data;
    return true;
}
void CLQ_Print(LNode* rear)
// 打印队列
{
    if (CLQ_IsEmpty(rear))  {
        printf("The queue is: empty. \n");
        return;
    }
    LNode* node=rear->next->next;
    do {
        printf("%d  ", node->data);
        node = node->next;
    }   while (node != rear->next); 
    //printf("\n");
}


第1关 顺序存储的栈

#include <stdio.h>
#include <stdlib.h>
#include "SeqStack.h"
/*创建一个栈*/
SeqStack* SS_Create(int maxlen)
{
    SeqStack* ss=(SeqStack*)malloc(sizeof(SeqStack));
    ss->data=(T*)malloc(maxlen*sizeof(T));
    ss->top=-1;
    ss->max=maxlen;
    return ss;
}
/*释放一个栈*/
void SS_Free(SeqStack* ss)
{
    free(ss->data);
    free(ss);
}
/*清空一个栈*/
void SS_MakeEmpty(SeqStack* ss)
{
    ss->top=-1;
}
bool SS_IsFull(SeqStack* stack)
// 判断栈是否为满。为满返回true,否则返回false。
{
    return stack->top+1>=stack->max;
}
bool SS_IsEmpty(SeqStack* stack)
// 判断栈是否为空。为空返回true,否则返回false。
{
    return stack->top<0;
}
int SS_Length(SeqStack* stack)
// 获取栈元素个数
{
    return stack->top+1;
}
bool SS_Push(SeqStack* stack, T x)
// 将元素x进栈,若满栈则无法进栈,返回false,否则返回true
{
    if (SS_IsFull(stack)) 
    {
        return false;
    }
    stack->top++;
    stack->data[stack->top]=x;
    return true;
}
bool SS_Pop(SeqStack* stack, T &item)
// 出栈的元素放入item。若出栈成功(栈不为空),则返回true;否则(空栈),返回false。
{
    if (SS_IsEmpty(stack)) 
    {
        return false;
    }
    item = stack->data[stack->top];
    stack->top--;
    return true;
}
/*获取栈顶元素放入item中,空栈则返回false*/
bool SS_Top(SeqStack* ss, T & item)
{
    if (SS_IsEmpty(ss)) 
    {
        return false;
    }
    item = ss->data[ss->top];
    return true;
}
/*从栈底到栈顶打印出所有元素*/
void SS_Print(SeqStack* ss)
{
    if (SS_IsEmpty(ss)) 
    { 
        printf("stack data: Empty!\n");
        return;
    }
    printf("stack data (from bottom to top):");
    int curr=0;
    while(curr<=ss->top) 
    {
        printf(" %d", ss->data[curr]);
        curr++;
    }
    //printf("\n");
}

第2关 实现一个链接存储的栈

#include <stdio.h>
#include <stdlib.h>
#include "LnkStack.h"
/*创建栈*/
LinkStack* LS_Create()
{
    LinkStack* ls=(LinkStack*)malloc(sizeof(LinkStack));
    ls->top = NULL;
    ls->len = 0;
    return ls;
}
/*释放栈*/
void LS_Free(LinkStack* ls)
{
    LNode* curr = ls->top;
    while(curr) 
    {
        LNode* next = curr->next;
        free(curr);
        curr=next;
    }
    free(ls);
}
/*将栈变为空栈*/
void LS_MakeEmpty(LinkStack* ls)
{
    LNode* curr = ls->top;
    while(curr) 
    {
        LNode* next = curr->next;
        free(curr);
        curr=next;
    }
    ls->top = NULL;
    ls->len = 0;
}
/*判断栈是否为空*/
bool LS_IsEmpty(LinkStack* ls)
{
    return ls->len == 0;
}
/*获取栈的长度*/
int LS_Length(LinkStack* ls)
{
    return ls->len;
}
/*将x进栈*/
void LS_Push(LinkStack* ls, T x)
{
    LNode* node=(LNode*)malloc(sizeof(LNode));
    node->data=x;
    node->next=ls->top;
    ls->top = node;
    ls->len ++;
}
/*出栈。出栈元素放入item;如果空栈,将返回false*/
bool LS_Pop(LinkStack* ls, T& item)
{
    LNode* node=ls->top;
    if (node==NULL) 
    {
        return false;
    }
    item = node->data;
    ls->top = node->next;
    ls->len --;
    free(node);
    return true;
}
/*读栈顶元素放入item。如果空栈,将返回false*/
bool LS_Top(LinkStack* ls, T& item)
{
    LNode* node=ls->top;
    if (node==NULL) 
    {
        return false;
    }
    item = node->data;
    return true;
}
/*从栈顶到栈底打印各结点数据*/
void LS_Print(LinkStack* ls)
{
    if (ls->len==0)
    { 
        printf("The stack: Empty!");
        return;
    }
    printf("The stack (from top to bottom):");
    LNode* curr=ls->top;
    while(curr) 
    {
        printf(" %d", curr->data);
        curr=curr->next;
    }
   // printf("\n");
}
  • 29
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bessie_Lee_gogogo

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值