错误与改进

    • 函数题
      • 实验8-1-4 使用函数的选择法排序
      • 实验8-1-7 数组循环右移
      • 实验8-1-8 报数
      • 实验8-2-7 字符串的连接
      • 实验8-2-9 长整数转化成16进制字符串(*)
      • 实验10-5 递归求简单交错幂级数的部分和
      • 实验10-9 递归将十进制转换二进制
      • 实验10-10 递归实现顺序输出整数
      • 实验11-1-6 指定位置输出字符串
      • 实验11-1-8 查找子串
      • 实验11-2-5 链表拼接
      • 实验11-2-6 奇数值结点链表
      • 实验11-2-9 链表逆置
    • 编程题
      • 实验2-2-9 计算火车运行时间
      • 实验3-1 求一元二次方程的根
      • 实验3-3 比较大小(*)
      • 实验4-1-12 黑洞数
      • 实验4-2-6 输出三角形字符阵列
      • 实验4-2-8 输出整数各位数字
      • 实验6-8 简单计算器
      • 实验7-1-1 简化的插入排序
      • 实验7-1-11 求整数序列中出现次数最多的数
      • 实验7-1-12 组个最小数
      • 实验7-1-13 装箱问题
      • 实验7-2-6 打印杨辉三角
      • 实验7-2-8 找鞍点
      • 实验7-2-9 螺旋方阵(**)
      • 实验7-2-10 简易连连看
      • 实验7-3-4 字符串替换
      • 实验7-3-6 字符串转换成十进制整数
      • 实验7-3-10 删除重复字符
      • 实验8-1-9 输出学生成绩
      • 实验9-5 查找书籍
      • 实验9-10 平面向量加法
      • 实验11-1-1 英文单词排序
      • 实验11-1-7 藏头诗

函数题

实验8-1-4 使用函数的选择法排序

本题要求实现一个用选择法对整数数组进行简单排序的函数。

函数接口定义

void sort( int a[], int n );

其中a是待排序的数组,n是数组a中元素的个数。该函数用选择法将数组a中的元素按升序排列,结果仍然在数组a中。

CODE

**记录这题就是想说,数组的形参a实际上是一个指针,主函数传递的是数组a的基地址。
**在函数中,既可以用a[ i ],也可以用 *(a+i)。

#include <stdio.h>
#define MAXN 10

void sort( int a[], int n );

int main()
{
    int i, n;
    int a[MAXN];

    scanf("%d", &n);
    for( i=0; i<n; i++ )
        scanf("%d", &a[i]);

    sort(a, n);

    printf("After sorted the array is:");
    for( i = 0; i < n; i++ )
        printf(" %d", a[i]);
    printf("\n");

    return 0;
}

/* 你的代码将被嵌在这里 */
void sort( int a[], int n )
{
    int index, tmp;
    for ( int i=0; i<n-1; i++ ){
        index = i;
        for ( int j=i+1; j<n; j++ ){
            if ( a[j]<a[index] ) index = j;
        }
        tmp = a[i];
        a[i] = a[index];
        a[index] = tmp;
    }
}

实验8-1-7 数组循环右移

本题要求实现一个对数组进行循环右移的简单函数:一个数组a中存有n(>0)个整数,将每个整数循环向右移m(≥0)个位置,即将a中的数据由(a​0​​a​1​​⋯a​n−1​​)变换为(a​n−m​​⋯a​n−1​​a​0​​a​1​​⋯a​n−m−1​​)(最后m个数循环移至最前面的m个位置)。

函数接口定义

int ArrayShift( int a[], int n, int m );

其中a[]是用户传入的数组;n是数组的大小;m是右移的位数。函数ArrayShift须将循环右移后的数组仍然存在a[]中。

**不过,这题有个奇怪的地方,循环右移并不需要返回值耶,题目却设为int,是不是一个bug
**而函数体中没有写return语句,实际上会返回一个不确定的值

CODE 1

**依次循环右移

#include <stdio.h>
#define MAXN 10

int ArrayShift( int a[], int n, int m );

int main()
{
    int a[MAXN], n, m;
    int i;

    scanf("%d %d", &n, &m);
    for ( i = 0; i < n; i++ ) scanf("%d", &a[i]);

    ArrayShift(a, n, m);

    for ( i = 0; i < n; i++ ) {
        if (i != 0) printf(" ");
        printf("%d", a[i]);
    }
    printf("\n");

    return 0;
}

/* 你的代码将被嵌在这里 */
int ArrayShift( int a[], int n, int m )
{
    int tmp;
    m %= n;
    for ( int i=0; i<m; i++ ){ //右移m次
        tmp = a[n-1]; //每次记录下最后一个数
        for ( int j=n-1; j>0; j-- ){ //前面的数全部后移一位
            a[j] = a[j-1];
        }
        a[0] = tmp; //把原来的最后一位赋给右移后的第一位
    }
}

CODE 2

**开辟一个新的数组做副本

int ArrayShift( int a[], int n, int m )
{
    int tmp[MAXN];
    m %= n;
    for ( int i=0; i<n; i++ ){
        tmp[i] = a[i];
    }
    for ( int i=0; i<n; i++ ){
        a[i] = tmp[(i+n-m)%n];
    }
}

实验8-1-8 报数

报数游戏是这样的:有n个人围成一圈,按顺序从1到n编好号。从第一个人开始报数,报到m(<n)的人退出圈子;下一个人从1开始报数,报到m的人退出圈子。如此下去,直到留下最后一个人。
本题要求编写函数,给出每个人的退出顺序编号。

函数接口定义

void CountOff( int n, int m, int out[] );

其中n是初始人数;m是游戏规定的退出位次(保证为小于n的正整数)。函数CountOff将每个人的退出顺序编号存在数组out[]中。因为C语言数组下标是从0开始的,所以第i个位置上的人是第out[i-1]个退出的。

裁判测试程序样例

#include <stdio.h>
#define MAXN 20

void CountOff( int n, int m, int out[] );

int main()
{
    int out[MAXN], n, m;
    int i;

    scanf("%d %d", &n, &m);
    CountOff( n, m, out );   
    for ( i = 0; i < n; i++ )
        printf("%d ", out[i]);
    printf("\n");

    return 0;
}

/* 你的代码将被嵌在这里 */

输入样例

11 3

输出样例

4 10 1 7 5 2 11 9 3 6 8 

CODE

**这题主要是要理解题意,out[i]记录的是第i+1个人是第几个退出的( ̄▽ ̄)"

void CountOff( int n, int m, int out[] )
{
    int i, a[MAXN];
    int j=1; //记录退出顺序
    int cnt = 0; //报数次数
    //记录这n个人顺序编号
    for ( i=0; i<n; i++ ){
        a[i] = i+1;
    }
    for ( i=0; j<=n; i++ ){ //很坑的是,说是留下一个人,但out[]却算了最后一个人
        if ( a[i]!=-1 ) //如果不是被剔除的人的位置,则参与报数
            cnt++; //报数+1
        if ( cnt==m ){
            out[i] = j; //记录下退出者的编号
            j++;
            a[i] = -1; //从编号中剔除
            //n--; //从报数人数中剔除
            cnt = 0; //重置报数次数
        }
        if ( i==n-1 ) //报数报完一轮,则重新开始
            i = -1; //注意,下一轮报数前会进行i++
    }
}

实验8-2-7 字符串的连接

函数接口定义

char *str_cat( char *s, char *t );

函数str_cat应将字符串t复制到字符串s的末端,并且返回字符串s的首地址。

CODE

**这题有个地方:for循环条件判断那里,如果不用n代换,而是直接二者相加,就会出现测试点1——超长字符串相接,发生运行时错误(如数组越界访问等)。
**因为s[ ] 一直在变长。

#include <stdio.h>
#include <string.h>

#define MAXS 10

char *str_cat( char *s, char *t );

int main()
{
    char *p;
    char str1[MAXS+MAXS] = {'\0'}, str2[MAXS] = {'\0'};

    scanf("%s%s", str1, str2);
    p = str_cat(str1, str2);
    printf("%s\n%s\n", p, str1);

    return 0;
}

/* 你的代码将被嵌在这里 */
char *str_cat( char *s, char *t )
{
    int i, j=0;
    int n = strlen(s) + strlen(t);
    for ( i=strlen(s); i<n; i++, j++ ){  //---(*)
        s[i] = t[j];
    }
    s[i] = '\0';
    return s;
}

实验8-2-9 长整数转化成16进制字符串(*)

函数接口定义

void f( long int x, char *p );

其中x是待转化的十进制长整数,p指向某个字符数组的首元素。函数f的功能是把转换所得的16进制字符串写入p所指向的数组。16进制的A~F为大写字母。

CODE

**这题需要注意:

  1. 指针是字符型的,所以要赋给它字符型常量或变量的值
  2. 十进制数是long int型的,所以副本也应定义为long int
#include <stdio.h>
#define MAXN 10

void f( long int x, char *p );

int main()
{
    long int x;
    char s[MAXN] = "";

    scanf("%ld", &x);
    f(x, s);
    printf("%s\n", s);

    return 0;
}

/* 你的代码将被嵌在这里 */
void f( long int x, char *p )
{
    int len = 0; //十六进制数的长度
    long int tmp; //复制十进制数x
    int m; //转进制时取余
    //零单独拎出来
    if ( x==0 )
        *p = '0';
    //负数加负号
    if ( x<0 ){
        *p = '-';
        x = -x;
        len++;
    }
    //正数或者本来是负数
    tmp = x; //副本
    if ( x>0 ){
        //计算长度
        do{
            x /= 16;
            len++;
        }while( x!=0 );
        x = tmp;
        //转进制
        for ( int i=0; i<MAXN; i++ ){
            m = x % 16;
            x /= 16;
            //将转换后的“余数”倒着放入数组中
            if ( m<=9 ) *(p+len-1-i) = m + '0';
            else *(p+len-1-i) = m -10 + 'A';
            if ( x==0 ) break;
        }
    }
}

实验10-5 递归求简单交错幂级数的部分和

本题要求实现一个函数,计算下列简单交错幂级数的部分和:
f(x,n)=x−x​2​​+x​3​​−x​4​​+⋯+(−1)​n−1​​x​n​​

函数接口定义

double fn( double x, int n );

其中题目保证传入的n是正整数,并且输入输出都在双精度范围内。函数fn应返回上述级数的部分和。建议尝试用递归实现。

CODE

**注意,fn()函数中不要在递归前区分加减,应该在幂函数项前控制加减

#include <stdio.h>

double fn( double x, int n );

int main()
{
    double x;
    int n;

    scanf("%lf %d", &x, &n);
    printf("%.2f\n", fn(x,n));

    return 0;
}

/* 你的代码将被嵌在这里 */
double ppow( double x, int n )
{
    if ( n==1 ) return x;
    else return x*ppow(x,n-1);
}
double fn( double x, int n )
{
    if ( n==1 ) return x;
    else if ( n%2 ) return ppow(x,n)+fn(x,n-1);
    else return -ppow(x,n)+fn(x,n-1);
}

实验10-9 递归将十进制转换二进制

本题要求实现一个函数,将正整数n转换为二进制后输出。

函数接口定义

void dectobin( int n );

函数dectobin应在一行中打印出二进制的n。建议用递归实现。

CODE

**这里需要理解,十进制转二进制时,除以2,最终十进制数都会变成0或1;而后依次向上取余

#include <stdio.h>

void dectobin( int n );

int main()
{
    int n;

    scanf("%d", &n);
    dectobin(n);

    return 0;
}

/* 你的代码将被嵌在这里 */
void dectobin( int n )
{
    if ( n==0 ) printf("0");
    else if ( n==1 ) printf("1");
    else{
        dectobin(n/2); //最先打印最底层(最高位)
        printf("%d", n%2); //再打印上面一层
    }
}

实验10-10 递归实现顺序输出整数

本题要求实现一个函数,对一个整数进行按位顺序输出。

函数接口定义

void printdigits( int n );

函数printdigits应将n的每一位数字从高位到低位顺序打印出来,每位数字占一行。

CODE

**深入理解上一实验

void printdigits( int n )
{
    if ( n/10==0 ) printf("%d\n", n);
    else{
        printdigits(n/10);
        printf("%d\n", n%10);
    }
}

实验11-1-6 指定位置输出字符串

本题要求实现一个函数,对给定的一个字符串和两个字符,打印出给定字符串中从与第一个字符匹配的位置开始到与第二个字符匹配的位置之间的所有字符。

函数接口定义

char *match( char *s, char ch1, char ch2 );

函数match应打印s中从ch1到ch2之间的所有字符,并且返回ch1的地址。

裁判测试程序样例

#include <stdio.h>

#define MAXS 10

char *match( char *s, char ch1, char ch2 );

int main()
{
    char str[MAXS], ch_start, ch_end, *p;

    scanf("%s\n", str);
    scanf("%c %c", &ch_start, &ch_end);
    p = match(str, ch_start, ch_end);
    printf("%s\n", p);

    return 0;
}

/* 你的代码将被嵌在这里 */

CODE 1

**出错:测试点1——ch1找不到,ch2找到
**改成一个return即可;返回NULL即有两个回车,返回s+i就有两个回车加一个空行

char *match( char *s, char ch1, char ch2 )
{
    int i, j;
    for ( i=0; s[i]!='\0'; i++ ){
        if ( s[i]==ch1 ){
            for ( j=i; s[j]!='\0'; j++ ){
                printf("%c", s[j]);
                if ( s[j]==ch2 ) break;
            }
            break;
        }
    }
    printf("\n");
    //if ( s[i]=='\0' ) return NULL;
    //else return s+i;
    return s+i;  
}

CODE 2

char *match( char *s, char ch1, char ch2 )
{
    char *t = "";
    int i = 0, mark = -1;
    while ( s[i] && s[i]!=ch1 ) i++;
    if ( s[i]==ch1 ){
        t = s + i;
        mark = i;
    }
    while ( s[i] && s[i]!=ch2 ) i++;
    if ( s[i]!=ch2 ) i--;
    if ( mark!=-1 ){
        while( mark<=i ){
            putchar(s[mark]);
            mark++;
        }
    }
    putchar('\n');
    return t;
}

实验11-1-8 查找子串

函数接口定义

char *search( char *s, char *t );

函数search在字符串s中查找子串t,返回子串t在s中的首地址。若未找到,则返回NULL。

裁判测试程序样例

#include <stdio.h>
#define MAXS 30

char *search(char *s, char *t);
void ReadString( char s[] ); /* 裁判提供,细节不表 */

int main()
{
    char s[MAXS], t[MAXS], *pos;

    ReadString(s);
    ReadString(t);
    pos = search(s, t);
    if ( pos != NULL )
        printf("%d\n", pos - s);
    else
        printf("-1\n");

    return 0;
}

/* 你的代码将被嵌在这里 */

CODE 1

**直接调用strstr(str1, str2)函数,如果str2是str1子串,则返回str2在str1中的首地址;否则返回NULL

#include <string.h>
char *search( char *s, char *t )
{
    return strstr(s,t);
}

CODE 2

**出错:测试点3——长度超过题面MAXS, t在结尾处

char *search( char *s, char *t )
{
    char *p = NULL;
    int i, j=0;
    int flag = 0;
    
    for ( i=0; s[i]!='\0'; i++ ){
        while( s[i] && t[j] && s[i]!=t[j] ) i++;
        if ( s[i]=='\0' ) break;
        else if ( s[i]==t[j] ){
        	p = s+i;
        	while( s[i] && t[j] ){
	            if ( s[i]!=t[j] ) break;
	            i++;
	            j++;
	        }
	        if ( t[j]=='\0' ) flag = 1;
	        if ( flag==0 ) p = NULL;
	        break;
		}
    }
    return p;
}

更改后的CODE 2

**做了注解以后,理清思路,发现是break位置放错了… 即应该在有子串之后退出循环
**本来没有(*)的i–,后来在基础编程题目集 7-29 删除字符串中的子串中发现了错误,才加上(此例的测试点没能测出这个错误)

char *search( char *s, char *t )
{
    char *p = NULL;  //默认为NULL
    int i, j;
    int flag = 0;  //有子串为1
    
   for ( i=0; s[i]!='\0'; i++ ){
        j = 0;  //注意,每一次比对都从第一个字符开始
        while( s[i] && t[j] && s[i]!=t[j] ) i++;  //找到第一个相同的字符
        if ( s[i]=='\0' ) break;  //都没找到
        else if ( s[i]==t[j] ){  //找到了
        	p = s+i;  //赋予首地址
        	while( s[i] && t[j] ){  //循环至不同了或者t[]结束了或者s[]先结束了
	            if ( s[i]!=t[j] ){
	            	i--;      //(*)退回到之前相等的最后一位,因为进入下一轮for循环还要i++,要保证不对等的这一位与t的第一位可以进行对比
	            	break;  //在这里结束是因为t[]扫描到'\0'了
	            }
	            i++;
	            j++;
	        }
	        if ( t[j]=='\0' ){
                flag = 1;  //如果是t[]扫描到'\0'了,标记为有子串
                break;  //有子串了即可退出
            }
	        if ( flag==0 ) p = NULL;  //没有子串需要置p为NULL
		}
    }
    return p;
}

实验11-2-5 链表拼接

本题要求实现一个合并两个有序链表的简单函数。链表结点定义如下:

struct ListNode {
    int data;
    struct ListNode *next;
};

函数接口定义

struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2);

其中list1和list2是用户传入的两个按data升序链接的链表的头指针;函数mergelists将两个链表合并成一个按data升序链接的链表,并返回结果链表的头指针。

裁判测试程序样例

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

struct ListNode {
    int data;
    struct ListNode *next;
};

struct ListNode *createlist(); /*裁判实现,细节不表*/
struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2);
void printlist( struct ListNode *head )
{
     struct ListNode *p = head;
     while (p) {
           printf("%d ", p->data);
           p = p->next;
     }
     printf("\n");
}

int main()
{
    struct ListNode  *list1, *list2;

    list1 = createlist();
    list2 = createlist();
    list1 = mergelists(list1, list2);
    printlist(list1);
	
    return 0;
}

/* 你的代码将被嵌在这里 */

CODE

**就挺头秃的,但理出来以后蛮好玩的( ̄▽ ̄)"

struct ListNode *mergelists(struct ListNode *list1, struct ListNode *list2)
{
    struct ListNode *head, *p, *ptr, *tmp;  //head指向合并后的队列的头节点;p指向新的节点;ptr用来指向待合并队列(非新节点所在队列);tmp是交换指针时来帮忙的
    
    /*空表*/
    if ( list1==NULL && list2==NULL ) return NULL;
    else if ( list1==NULL ) return list2;
    else if ( list2==NULL ) return list1;
    
    /*确定头指针*/
    if ( list1->data <= list2->data ){  //从list1开始合并
        head = list1;  //头指针指向list1头节点
        p = list1;  //list1头节点成为第一个新的节点
        ptr = list2;
    }
    else{  //从list2开始合并
        head = list2;
        p = list2;
        ptr = list1;
    }
    
    /*开始遍历了!*/
    while(1){
        while ( p->next!=NULL && p->next->data <= ptr->data ){    //新节点所在队列的下一个节点非空;下一个节点的数据仍然比待合并队列的小
            p = p->next;  //新节点仍是这个队列中的
        }
        if ( p->next==NULL ){   //如果前面是因为队列遍历完了,那合并队列后面的部分都是待合并队列的内容
            p->next = ptr;   //待合并列接过来,就可以结束了
            return head;
        }
        else{    //待合并队列没有结束,交换指向合并队列和待合并队列的指针
            
//step1          step2          step3          step4          即交换后为
//    p tmp          p tmp           tmp          tmp,ptr           p
//    ↓ ↓            ↓ ↓              ↓              ↓              ↓
//1→2→3→8→9→10   1→2→3 8→9→10   1→2→3 8→9→10   1→2→3 8→9→10   1→2→3→4→5→6→7
//                 ↙             ↙             ↙
//4→5→6→7        4→5→6→7        4→5→6→7        4→5→6→7        8→9→10
//↑              ↑              ↑              ↑              ↑
//ptr           ptr           ptr,p            p           tmp,ptr
            
            tmp = p->next;   //存储交换前的p的下一个节点所在位置,即将成为待合并队列的部分
            p->next = ptr;  //把待合并ptr队列接到合并队列后面去,成为合并队列
            p = ptr;   //让p指向新加入合并队列的节点
            ptr = tmp;   //ptr指向之前存储的待合并队列
        }
    }
    
}

实验11-2-6 奇数值结点链表

本题要求实现两个函数,分别将读入的数据存储为单链表、将链表中奇数值的结点重新组成一个新的链表。链表结点定义如下:

struct ListNode {
    int data;
    ListNode *next;
};

函数接口定义

struct ListNode *readlist();
struct ListNode *getodd( struct ListNode **L );

函数readlist从标准输入读入一系列正整数,按照读入顺序建立单链表。当读到−1时表示输入结束,函数应返回指向单链表头结点的指针。
函数getodd将单链表L中奇数值的结点分离出来,重新组成一个新的链表。返回指向新链表头结点的指针,同时将L中存储的地址改为删除了奇数值结点后的链表的头结点地址(所以要传入L的指针)。

裁判测试程序样例

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

struct ListNode {
    int data;
    struct ListNode *next;
};

struct ListNode *readlist();
struct ListNode *getodd( struct ListNode **L );
void printlist( struct ListNode *L )
{
     struct ListNode *p = L;
     while (p) {
           printf("%d ", p->data);
           p = p->next;
     }
     printf("\n");
}

int main()
{
    struct ListNode *L, *Odd;
    L = readlist();
    Odd = getodd(&L);
    printlist(Odd);
    printlist(L);

    return 0;
}

/* 你的代码将被嵌在这里 */

CODE

**注意:(*tail)->next 必须带括号!!!
** 这题的特别之处在于函数形参需要用二级指针
** 一级指针变量存放别人的地址;二级指针变量存放别人”存放地址“的地方
** 在函数中新分配一块内存空间给指针变量p,如果用一级指针指向p,该一级指针只是存了p指向的值的地址;而只有二级指针指向p,才能真正存好p所在位置
** 一开始出错,read以后print出来是空的,就是因为InsertList的参数用的一级指针,实际上没有把传入的节点和新节点链接起来

struct ListNode *InsertList(struct ListNode **head, struct ListNode **tail, int data)
{
    struct ListNode *p;
    int size = sizeof(struct ListNode);
    
    p = (struct ListNode *)malloc(size);
    p->data = data;
    
    if ( (*head)==NULL ){
        (*head) = p;
        //p->next = NULL;
    }
    else{
        (*tail)->next = p;
    }
    p->next = NULL;
    (*tail) = p;
    
    return *head;
}
struct ListNode *readlist()
{
    struct ListNode *head, *tail;
    int data;
    
    head = tail = NULL;
    scanf("%d", &data);
    while ( data!=-1 ){
        InsertList(&head,&tail,data);   //调用插入函数
        scanf("%d", &data);
    }
    
    return head;
}
struct ListNode *getodd( struct ListNode **L )
{
    struct ListNode *h1, *h2, *t1, *t2;  //p1,t1分别为奇数表的头节点和尾节点;p2,t2分别为原表删除奇数节点后的头节点和尾节点
    int data;
    
    /*空表*/
    if ( (*L)==NULL ) return NULL;
    
    /*非空*/
    h1 = t1 = NULL;  //初始化空奇数表
    h2 = t2 = NULL;  //初始化删除奇数节点后的新表
    while ( (*L)!=NULL ){
        data = (*L)->data;
        
        if ( data%2 ){
            InsertList(&h1,&t1,data);
        }
        else
            InsertList(&h2,&t2,data);
        *L = (*L)->next;   //L后移
    }
    
    *L = h2;   //原链表更新为h2链表
    return h1;
}

实验11-2-9 链表逆置

本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头。链表结点定义如下:

struct ListNode {
    int data;
    struct ListNode *next;
};

函数接口定义

struct ListNode *reverse( struct ListNode *head );

其中head是用户传入的链表的头指针;函数reverse将链表head逆置,并返回结果链表的头指针。

裁判测试程序样例

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

struct ListNode {
    int data;
    struct ListNode *next;
};

struct ListNode *createlist(); /*裁判实现,细节不表*/
struct ListNode *reverse( struct ListNode *head );
void printlist( struct ListNode *head )
{
     struct ListNode *p = head;
     while (p) {
           printf("%d ", p->data);
           p = p->next;
     }
     printf("\n");
}

int main()
{
    struct ListNode  *head;

    head = createlist();
    head = reverse(head);
    printlist(head);
	
    return 0;
}

/* 你的代码将被嵌在这里 */

CODE

核心:指针往前指导致与后面的链表脱节,故应提前保存好后面链表的头节点和前面链表的尾节点
** 后面链表的头节点可能是NULL,所以逆序后链表的头节点跟前面链表的尾节点一致

struct ListNode *reverse( struct ListNode *head )
{
    struct ListNode *p, *tmp;
    
    if ( head==NULL || head->next==NULL ) return head;
    
    p = head->next;   //从头节点的下一个节点开始遍历
    head->next = NULL;   //原来的头指针改为指向NULL,即变成尾节点
    while( p!=NULL ){
        tmp = p;   //保存p
        p = p->next;   //p后移一位
        tmp->next = head;   //与p断开,指向前面的节点
        head = tmp;   //head后移一位
    }
    
    return head;
}

编程题

实验2-2-9 计算火车运行时间

输入格式
输入在一行中给出2个4位正整数,其间以空格分隔,分别表示火车的出发时间和到达时间。每个时间的格式为2位小时数(00-23)和2位分钟数(00-59),假设出发和到达在同一天内。

输出格式
在一行输出该旅途所用的时间,格式为“hh:mm”,其中hh为2位小时数、mm为2位分钟数。

输入样例

1201 1530

输出样例

03:29

思路

  1. 先从输入中提取出时与分
  2. 根据分钟数之差是否需要向小时借60分钟,分别计算

CODE

int time1, time2;
int h1, h2, m1, m2;
int h, m;

scanf("%d%d", &time1, &time2);
for ( int i=1; i<=2; i++ ){
    if ( i==1 ) m1 = time1%10;
    else m1 += (time1%10) * 10;
    time1 /= 10;
}
h1 = time1;
for ( int i=1; i<=2; i++ ){
    if ( i==1 ) m2 = time2%10;
    else m2 += (time2%10) * 10;
    time2 /= 10;
}
h2 = time2;

if ( m2>=m1 ){
    m = m2 - m1;
    h = h2 - h1;
}
else{
    m = m2 + 60 - m1;
    h = h2 - 1 - h1;
}

printf("%02d:%02d", h, m);

看着冗余了些…

改进

  1. 取分:直接对100取余即可;取时:直接除以100即可
  2. 计算时差可以保留原来的方法,也可以直接相减,之后再判断分钟是否为负

更改后的CODE

int time1, time2;
//     int h1, h2, m1, m2;
int h, m;
    
scanf("%d%d", &time1, &time2);
//     m1 = time1 % 100;
//     h1 = time1 / 100;
//     m2 = time2 % 100;
//     h2 = time2 / 100;    
m = time2 % 100 - time1 % 100;
h = time2 / 100 - time1 / 100;

if ( m<0 ){
    m += 60;
    h--;
}

printf("%02d:%02d", h, m);

实验3-1 求一元二次方程的根

本题目要求一元二次方程的根,结果保留2位小数。

输入格式:
输入在一行中给出3个浮点系数a、b、c,中间用空格分开。

输出格式:
根据系数情况,输出不同结果:
1)如果方程有两个不相等的实数根,则每行输出一个根,先大后小;
2)如果方程有两个不相等复数根,则每行按照格式“实部+虚部i”输出一个根,先输出虚部为正的,后输出虚部为负的;
3)如果方程只有一个根,则直接输出此根;
4)如果系数都为0,则输出"Zero Equation";
5)如果a和b为0,c不为0,则输出"Not An Equation"。

CODE

小细节:当b=0时,- b = - 0

#include <stdio.h>
#include <math.h>

int main()
{
    double a, b, c;
    double delta=0, x1=0, x2=0;
    double real=0, imag=0;
    
    scanf("%lf%lf%lf", &a, &b, &c);
    delta = b*b - 4*a*c;
    if ( a==0 && b==0 && c==0 ){
        printf("Zero Equation");
    }
    else if ( a==0 && b==0 && c!=0 ){
        printf("Not An Equation");
    }
    else if ( a!=0 && delta>0 ){ //有两不相等实根,还需要a!=0
        x1 = (-b + sqrt(delta)) / (2*a);
        x2 = (-b - sqrt(delta)) / (2*a);
        if ( x2>x1 ) printf("%.2f\n%.2f", x2, x1);
        else printf("%.2f\n%.2f", x1, x2);
    }
    //有两相等实根,有两种情况
    else if ( a!=0 && delta==0 ){
        x1 = x2 = (-b) / (2*a);
        printf("%.2f", x1);
    }
    else if ( a==0 && delta>0 ){
        x1 = x2 = (-c) / b;
        printf("%.2f", x1);
    }
    else if ( a!=0 && delta<0 ){ //有两不相等复数根,还需要a!=0
        delta = -delta;
        real = (-b) / (2*a);
        imag = sqrt(delta) / (2*a);
        if ( b==0 ) real = 0; //这句很关键,因为前面的-b或者(-1)*b都会使其结果为-0.00
        printf("%.2lf+%.2lfi\n", real, imag);
	    printf("%.2lf%.2lfi", real, -imag);
    }
    
    return 0;
}

实验3-3 比较大小(*)

本题要求将输入的任意3个整数从小到大输出。

** 今天发现一个有意思的两变量交换值(int)的方法(●ˇ∀ˇ●)
不需要第三方变量交换值的方法有:

  1. 按位异或
  2. 相互加减(自己瞎起的( ̄▽ ̄)" 不知道叫啥)

CODE 1

int a, b, c;
    
scanf("%d%d%d", &a, &b, &c);
//使第一个比后两个小
if ( a>b ){ //使第一个比第二个小
    a ^= b;
    b ^= a;
    a ^= b;
}
if ( a>c ){ //使第一个比第三个小
    a ^= c;
    c ^= a;
    a ^= c;
}
//使第二个比后一个小
if ( b>c ){
    b ^= c;
    c ^= b;
    b ^= c;
}

printf("%d->%d->%d", a, b, c);

return 0;

CODE 2

a = a + b;
b = a - b;
a = a - b;

实验4-1-12 黑洞数

黑洞数也称为陷阱数,又称“Kaprekar问题”,是一类具有奇特转换特性的数。

任何一个各位数字不全相同的三位数,经有限次“重排求差”操作,总会得到495。最后所得的495即为三位黑洞数。所谓“重排求差”操作即组成该数的数字重排后的最大数减去重排后的最小数。(6174为四位黑洞数。)

例如,对三位数207:

第1次重排求差得:720 - 27 = 693;
第2次重排求差得:963 - 369 = 594;
第3次重排求差得:954 - 459 = 495;

以后会停留在495这一黑洞数。如果三位数的3个数字全相同,一次转换后即为0。
任意输入一个三位数,编程给出重排求差的过程。

输入样例

123

输出样例

1: 321 - 123 = 198
2: 981 - 189 = 792
3: 972 - 279 = 693
4: 963 - 369 = 594
5: 954 - 459 = 495

CODE

#include <stdio.h>

int Kaprekar(int n); //重排求差

int main()
{
    int n;
    int i=1;
    
    scanf("%d", &n);
    printf("%d: ", i);
    i++;
    n = Kaprekar(n);
    while( n!=495 ){
        printf("%d: ", i);
        i++;
        n = Kaprekar(n);
    }
    
    return 0;
}
int Kaprekar(int n)
{
    int t = n;
    int digit[3]; //每位数
    int cmp[6]; //重排的所有可能
    int i = 0, max, min;
    int ret; //返回重排后的差值
    
    do{
        digit[i] = t%10;
        t /= 10;
        i++;
    }while(t);
    
    cmp[0] = digit[0]*100 + digit[1]*10 + digit[2];
    cmp[1] = digit[0]*100 + digit[2]*10 + digit[1];
    cmp[2] = digit[1]*100 + digit[2]*10 + digit[0];
    cmp[3] = digit[1]*100 + digit[0]*10 + digit[2];
    cmp[4] = digit[2]*100 + digit[1]*10 + digit[0];
    cmp[5] = digit[2]*100 + digit[0]*10 + digit[1];
    
    max = cmp[0];
    min = cmp[0];
    for ( i=1; i<6; i++ ){
        if ( cmp[i]>max ) max = cmp[i];
        else if ( cmp[i]<min ) min = cmp[i];
    }
    ret = max - min;
    printf("%d - %d = %d\n", max, min, ret);
    
    return ret;
}

太冗余了又…

改进

  1. 每位数、重排、比较大小合并:比较每位数的大小,就知道重排的最大值和最小值了

更改后的CODE

**注意:输入495时,也需要重排一次

int n;
int k = 0; //重排差值
int a, b, c, t; //装位数
int i = 1; //输出题头
int max, min;

scanf("%d", &n);
k = n;
do{
    //获取位数
    a = k / 100; //百位
    b = (k%100) / 10; //十位
    c = k % 10; //个位
    //重排:从大到小
    if ( a<b ){ //让第一个数比后面两个数都小
        t = a; a = b; b = t;
    }
    if ( a<c ){
        t = a; a = c; c = t;
    }
    if ( b<c ){ //让第二个数比后面一个数小
        t = b; b = c; c = t;
    }
    //求差值
    max = a*100+b*10+c;
    min = c*100+b*10+a;
    k = max - min;
    //输出
    printf("%d: %d - %d = %d\n", i, max, min, k);
    i++;
}while( k!=495 );

实验4-2-6 输出三角形字符阵列

**这题,我就想记一下,字符型变量可以自增自减o( ̄▽ ̄)o

char ch = 'A';
ch++; //这时,ch = 'B'

输入:在一行中给出一个正整数n(1≤n<7)。

输出:n行由大写字母A开始构成的三角形字符阵列。格式见输出样例,其中每个字母后面都有一个空格。

输入样例

4

输出样例

A B C D 
E F G 
H I 
J 

CODE

char ch = 'A';
int n;

scanf("%d", &n);
for ( int i=1; i<=n; i++ ){
    for ( int j=n; j>=i; j-- ){
        printf("%c ", ch);
        ch++;
    }
    printf("\n");
}

实验4-2-8 输出整数各位数字

**这题,我第一次写的时候用数组装每位数,然后逆序+空格输出来着;这次就是试试更直接的字符数组(~ ̄▽ ̄)~
**但是,感觉这两个方法都是数组的长度很迷,只能开大一些;如果先统计输入数字的位数,而后直接按格式输出,可以避免这个问题(但感觉有点点麻烦哈哈哈( ̄▽ ̄)"

输入:在一行中给出一个长整型范围内的非负整数。

输出:从高位开始逐位输出该整数的各位数字,每个数字后面有一个空格。

#include <stdio.h>
#define N 1000
int main()
{
    char a[N];
    
    gets(a);
    for ( int i=0; a[i]; i++ ){
        printf("%c ", a[i]);
    }
    
    return 0;
}

实验6-8 简单计算器

模拟简单运算器的工作。假设计算器只能进行加减乘除运算,运算数和结果都是整数,四种运算符的优先级相同,按从左到右的顺序计算。

输入格式
输入在一行中给出一个四则运算算式,没有空格,且至少有一个操作数。遇等号”=”说明输入结束。

输出格式
在一行中输出算式的运算结果,或者如果除法分母为0或有非法运算符,则输出错误信息“ERROR”。

CODE

**记录这题就是想说,scanf输入数字后,是可以直接用getchar()直接收字符的

int a, b;
char op;
int illegal = 0; //不合法则为1

scanf("%d", &a);  //操作符左边的数,作为最后结果
op = getchar();  //读入操作符
while(op!='='){
    scanf("%d", &b);  //操作符右边的数
    if ( op=='+' ) a += b;
    else if ( op=='-' ) a -= b;
    else if ( op=='*' ) a *= b;
    else if ( op=='/' ){
        if ( b==0 ) illegal = 1; //不合法的标记
        else a /= b;
    }
    else illegal = 1; //不合法的标记
    
    if ( illegal ) break;
    op = getchar();
}
if ( illegal ) printf("ERROR");
else printf("%d", a);

实验7-1-1 简化的插入排序

输入格式
输入在第一行先给出非负整数N(<10);第二行给出N个从小到大排好顺序的整数;第三行给出一个整数X。

输出格式
在一行内输出将X插入后仍然从小到大有序的整数序列,每个数字后面有一个空格。

出错点

  1. 输入的N为非负整数,即需要特别注意N=0的情况
  2. N=0时,并不是没有后面的读入和输出了o_o …而是直接输出新插入的数( ̄▽ ̄)"

CODE

#include <stdio.h>
#define N 11

int main()
{
    int n, a[N], x;
    
    scanf("%d", &n);
    
    if ( n>0 ){
        for ( int i=0; i<n; i++ ){
            scanf("%d", &a[i]);
        }
        scanf("%d", &x);

        if ( x>a[n-1] ) a[n] = x; //插在最后一个数之后
        else{
            for ( int i=n-1; i>=0; i-- ){  //倒序查找,一一后移
                if ( x<=a[i] ){
                    a[i+1] = a[i];
                    a[i] = x;
                }
            }
        }

        for ( int i=0; i<n+1; i++ ){
            printf("%d ", a[i]);
        }
    }
    else{
        scanf("%d", &x);
        printf("%d ", x);  //注意后面的空格不要漏了
    }
    
    return 0;
}

实验7-1-11 求整数序列中出现次数最多的数

输入格式
输入在一行中给出序列中整数个数N(0<N≤1000),以及N个整数。数字间以空格分隔。

输出格式
在一行中输出出现次数最多的整数及其出现次数,数字间以空格分隔。题目保证这样的数字是唯一的。

CODE

** a[ ] 存放待统计的数;b[ ] 统计次数;二者的下标一一对应,即同一个下标,放在a[ ] 就是那个数,放在b[ ] 就是次数

#include <stdio.h>
#define N 1000
int main()
{
    int n, a[N], b[N]={0};
    int cnt = 0;
    int max;
    int i, j;
    
    scanf("%d", &n);
    for ( i=0; i<n; i++ ){
        scanf("%d", &a[i]);
    }
    
    for ( i=0; i<n; i++ ){ //用b[]统计每个数出现的次数
        for ( j=0; j<i; j++ ){ //和之前已经统计过的数比较,看是否相同
            if ( a[i]==a[j] ){
                b[j]++; //相同,则把次数加在之前统计的数上
                break; //即第二次出现的数对应的b[]=0
            }
        }
        if ( i==j ) b[i]++; //如果不同,则对下标为i的数的次数加1
    }
    
    max = 0;
    for ( int i=1; i<n; i++ ){
        if ( b[i]>b[max] ) max = i;
    }
    
    printf("%d %d", a[max], b[max]);
    
    return 0;
}

实验7-1-12 组个最小数

输入格式
输入在一行中给出10个非负整数,顺序表示我们拥有数字0、数字1、……数字9的个数。整数间用一个空格分隔。10个数字的总个数不超过50,且至少拥有1个非0的数字。

输出格式
在一行中输出能够组成的最小的数。

CODE

**每输出一次,就减少一次数;先输出第一位(要求非0),再按顺序输出后面的数

#include <stdio.h>
#define N 10
int main()
{
    int a[N];
    
    for ( int i=0; i<N; i++ ){
        scanf("%d", &a[i]);
    }
    //输出第一个非0的最小数
    for ( int i=1; i<N; i++ ){
        if ( a[i]!=0 ){
            printf("%d", i);
            a[i]--;
            break;
        }
    }
    //从小到大输出
    for ( int i=0; i<N; i++ ){
        while( a[i]!=0 ){
            printf("%d", i);
            a[i]--;
        }
    }
    
    return 0;
}

实验7-1-13 装箱问题

假设有N项物品,大小分别为s​1​​、s​2、…、s​i​​、…、s​N​​,其中s​i​​为满足1 ≤ s​i​​ ≤ 100的整数。要把这些物品装入到容量为100的一批箱子(序号1-N)中。装箱方法是:对每项物品, 顺序扫描箱子,把该物品放入足以能够容下它的第一个箱子中。请写一个程序模拟这种装箱过程,并输出每个物品所在的箱子序号,以及放置全部物品所需的箱子数目。

输入:第一行给出物品个数N(≤1000);第二行给出N个正整数s​i​​(1 ≤ s​i​​ ≤100,表示第i项物品的大小)。

输出:按照输入顺序输出每个物品的大小及其所在的箱子序号,每个物品占1行,最后一行输出所需的箱子数目。

输入样例

8
60 70 80 90 30 40 10 20

输出样例

60 1
70 2
80 3
90 4
30 1
40 5
10 1
20 2
5

CODE 1

**用结构类型数组来解决物品大小及其存放的箱子号问题。

#include <stdio.h>
#define N 1000

typedef struct info{
    int size; //物品大小
    int num; //箱子号
}info;

int main()
{
    int n;
    info item[N]; //物品信息
    int box[N] = {0}; //箱子号对应的已装数量
    int i, j;
    int cnt = 0; //记录已用箱子数量,初始为0
    
    scanf("%d", &n);
    for ( i=0; i<n; i++ ){
        scanf("%d", &item[i].size);
    }
    
    for ( i=0; i<n; i++ ){ //第i个item
        for ( j=0; j<n; j++ ){ //遍历box,且最多有n个箱子(1个item对应1个box)
            if ( (box[j]+item[i].size)<=100 ){
                box[j] += item[i].size; //装箱
                item[i].num = j+1; //记下箱子号(下标从1开始)
                if ( item[i].num>cnt ) cnt = item[i].num; //记录最大箱子号(下标从1开始)
                break;
            }
        }
    }
    
    for ( i=0; i<n; i++ ){
        printf("%d %d\n", item[i].size, item[i].num);
    }
    printf("%d", cnt); //下标从1开始
    
    return 0;
}

CODE 2

**用两个数组,下标一一对应,分别记录物品的大小和箱子号
**总体思路上,与前面相同

#include <stdio.h>
#define N 1000

int main()
{
    int n;
    int item[N], num[N]; //物品大小,对应箱子号
    int box[N] = {0}; //箱子号对应的已装数量
    int i, j;
    int cnt = 0; //记录已用箱子数量,初始为0
    
    scanf("%d", &n);
    for ( i=0; i<n; i++ ){
        scanf("%d", &item[i]);
    }
    
    for ( i=0; i<n; i++ ){ //第i个item
        for ( j=0; j<n; j++ ){ //遍历box
            if ( (box[j]+item[i])<=100 ){
                box[j] += item[i]; //装箱
                num[i] = j+1; //记下箱子号(下标从1开始)
                if ( num[i]>cnt ) cnt = num[i]; //记录最大箱子号(下标从1开始)
                break;
            }
        }
    }
    
    for ( i=0; i<n; i++ ){
        printf("%d %d\n", item[i], num[i]);
    }
    printf("%d", cnt); //下标从1开始
    
    return 0;
}

实验7-2-6 打印杨辉三角

输入格式
输入在一行中给出N(1≤N≤10)。

输出格式
以正三角形的格式输出前N行杨辉三角。每个数字占固定4位。

输入样例

6

输出样例

        1
       1   1
      1   2   1
     1   3   3   1
    1   4   6   4   1
   1   5  10  10   5   1

CODE

#include <stdio.h>
#define N 10

int main()
{
    int n, a[N][N];
    int i, j;
    
    scanf("%d", &n);
    //初始化边界元素1
    for ( i=0; i<n; i++ ){
        a[i][0] = 1; //第一列
        a[i][i] = 1; //主对角线
    }
    //初始化中间值
    for ( i=2; i<n; i++ ){
        for ( j=1; j<i; j++ ){
            a[i][j] = a[i-1][j-1] + a[i-1][j]; //等于前一行两项相加
        }
    }
    //输出
    for ( i=0; i<n; i++ ){
        for ( j=i; j<n-1; j++ ){
            printf(" ");
        }
        for ( j=0; j<=i; j++ ){
            printf("%4d", a[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

实验7-2-8 找鞍点

一个矩阵元素的“鞍点”是指该位置上的元素值在该行上最大、在该列上最小。
本题要求编写程序,求一个给定的n阶方阵的鞍点。

输入:第一行给出一个正整数n(1≤n≤6)。随后n行,每行给出n个整数,其间以空格分隔。

输出:在一行中按照“行下标 列下标”(下标从0开始)的格式输出鞍点的位置。如果鞍点不存在,则输出“NONE”。题目保证给出的矩阵至多存在一个鞍点。

CODE

** if 判断(*)和(**)用 >=和 <=是因为测试点2说,“有并列极值元素,最后一个是鞍点”… 傻笑.jpg ( ̄▽ ̄)"

#include <stdio.h>
#define N 6

int main()
{
    int n, a[N][N];
    int has = 0; //有鞍点则为1
    int max, min; //最大值所在列,最小值所在行
    int i, j;
    
    scanf("%d", &n);
    for ( i=0; i<n; i++ ){
        for ( j=0; j<n; j++ ){
            scanf("%d", &a[i][j]);
        }
    }
    
    for ( i=0; i<n; i++ ){
        max = 0; //第i行最大值,在第max列
        for ( j=1; j<n; j++ ){
            if ( a[i][j]>=a[i][max] ) max = j; //---(*)
        }
        min = 0; //第max列最小值,在第min行
        for ( j=1; j<n; j++ ){
            if ( a[j][max]<=a[min][max] ) min = j; //---(**)
        }
        if ( min==i ){
            has = 1;
            break; //最多一个鞍点
        }
    }
    
    if ( has ) printf("%d %d", min, max);
    else printf("NONE");
    
    return 0;
}

实验7-2-9 螺旋方阵(**)

所谓“螺旋方阵”,是指对任意给定的N,将1到N×N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入N×N的方阵里。本题要求构造这样的螺旋方阵。

输入:在一行中给出一个正整数N(<10)。

输出:N×N的螺旋方阵。每行N个数字,每个数字占3位。

输入样例

5

输出样例

  1  2  3  4  5
 16 17 18 19  6
 15 24 25 20  7
 14 23 22 21  8
 13 12 11 10  9

CODE

**按照螺旋路线,先分析第一圈怎么放入数组中;
**然后再看和第 j 圈有什么联系;
**最后看 n 阶矩阵需要螺旋几圈( j )。

#include <stdio.h>
#define N 10

int main()
{
    int n, a[N][N];
    int i, j=0;
    int num = 1;
    
    scanf("%d", &n);
    while( j<n/2 ){
        //→
        for ( i=j; i<n-1-j; i++ ){
            a[j][i] = num;
            num++;
        }
        //↓
        for ( i=j; i<n-1-j; i++ ){
            a[i][n-1-j] = num;
            num++;
        }
        //←
        for ( i=n-1-j; i>j; i-- ){
            a[n-1-j][i] = num;
            num++;
        }
        //↑
        for ( i=n-1-j; i>j; i-- ){
            a[i][j] = num;
            num++;
        }
        j++;
    }
    if ( n%2 ) a[n/2][n/2] = num;
    
    for ( i=0; i<n; i++ ){
        for ( j=0; j<n; j++ ){
            printf("%3d", a[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

实验7-2-10 简易连连看

**竟然会做连连看了!!!(●ˇ∀ˇ●) …长题警告(ง •_•)ง

本题要求实现一个简易连连看游戏模拟程序。
给定一个2N×2N的方阵网格游戏盘面,每个格子中放置一些符号。这些符号一定是成对出现的,同一个符号可能不止一对。程序读入玩家给出的一对位置(x​1​​,y​1​​)、(x​2​​,y​2​​),判断这两个位置上的符号是否匹配。如果匹配成功,则将两个符号消为“*”并输出消去后的盘面;否则输出“Uh-oh”。若匹配错误达到3次,则输出“Game Over”并结束游戏。或者当全部符号匹配成功,则输出“Congratulations!”,然后结束游戏。

输入格式
输入在一行中给一个正整数N(<5)。随后2N行,每行2N个大写英文字母(其间以1个空格分隔),表示游戏盘面。盘面之后给出一个正整数K,随后K行,每行按照格式“x​1​​ y​1​​ x​2​​ y​2​​”给出一个玩家的输入。注意格子的行、列编号是从1到2N。

输出格式
根据玩家的每一步输入,输出相应的结果。输出盘面时注意,每行字符间以1个空格分隔,行末不得有多余空格。

CODE

**一定要注意,输入int型变量后,要接收字符型变量前,一定要接收一下换行!!!虽然前面讲了很多次了,但是我一开始还是倒在了这里…ヽ(゜▽゜ )-C<(/;◇;)/~

#include <stdio.h>
#define N 10
#define M 100

int main()
{
    int n, m;
    char game[N][N];
    int x1, y1, x2, y2;
    int i, j, k;
    int right = 0; //连对的次数,用来判断全部连完
    int error = 0; //连错的次数,用来判断游戏结束
    
    scanf("%d", &n);
    getchar();
    n = 2 * n;
    for ( i=0; i<n; i++ ){
        for ( j=0; j<n; j++ ){
            game[i][j] = getchar();
            getchar();
        }
    }
    
    scanf("%d", &m);
    for ( i=0; i<m; i++ ){
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        if ( game[x1-1][y1-1]==game[x2-1][y2-1] && game[x1-1][y1-1]!='*' && game[x2-1][y2-1]!='*' ){ //这里注意排除'*'的情况
            right++; //连对次数增加
            if ( right==(n*n/2) ){
                printf("Congratulations!");
                break; //可能会有多余的输入,注意退出
            }
            else{
                game[x1-1][y1-1] = game[x2-1][y2-1] = '*';
                for ( j=0; j<n; j++ ){
                    for ( k=0; k<n; k++ ){
                        putchar(game[j][k]);
                        if ( k!=n-1 ) printf(" ");
                        else printf("\n");
                    }
                }
            }
        }
        else{
            error++; //连错次数增加
            printf("Uh-oh\n");
            if ( error==3 ){
                printf("Game Over");
                break;
            }
        }
    }
    
    return 0;
}

实验7-3-4 字符串替换

本题要求编写程序,将给定字符串中的大写英文字母按以下对应规则替换:
A→Z,B→Y,C→X,…,X→C,Y→B,Z→A

输入格式
输入在一行中给出一个不超过80个字符、并以回车结束的字符串。

输出格式
输出在一行中给出替换完成后的字符串。

CODE

**规律:str[ i ] - ‘A’ + ‘Z’,即替换成’Z’减去(该字母与’A’的差)

#include <stdio.h>
#define N 80

int main()
{
    char str[N];
    int i = 0;
    
    str[i] = getchar();
    while( str[i]!='\n' ){
        if ( str[i]>='A' && str[i]<='Z' )
            str[i] = 'A' - str[i] + 'Z';
        putchar(str[i]);
        i++;
        str[i] = getchar();
    }
    
    return 0;
}

实验7-3-6 字符串转换成十进制整数

输入一个以#结束的字符串,本题要求滤去所有的非十六进制字符(不分大小写),组成一个新的表示十六进制数字的字符串,然后将其转换为十进制数后输出。如果在第一个十六进制字符之前存在字符“-”,则代表该数是负数。题目保证输出在长整型范围内。

CODE

**这里有一个需要注意的点:
long和long long不同,分别对应的输出格式为%ld,%lld。

#include <stdio.h>
#define N 1000

int main()
{
    char str[N], ret[N];
    int i = 0, j = 0, k = 0; //k记录str[]的长度
    int index1=-1, index2=-1; //分别记录第一个负号和第一个十六进制数出现的位置
    int factor = 1; //十六进制转十进制计算的因子
    long long result = 0; //转换结果
    
    //输入
    str[k] = getchar();
    while( str[k]!='#' ){
        k++;
        str[k] = getchar();
    }
    //找到第一个负号
    for ( i=0; i<k; i++ ){
        if ( str[i]=='-' ){
            index1 = i;
            break;
        }
    }
    //找到第一个十六进制数
    for ( i=0; i<k; i++ ){
        if ( str[i]>='0' && str[i]<='9' || str[i]>='a' && str[i]<='f' || str[i]>='A' && str[i]<='F' ){
            index2 = i;
            break;
        }
    }
    //从第一个十六进制数开始存储
    for ( i=index2; i<k; i++ ){
        if ( str[i]>='0' && str[i]<='9' || str[i]>='a' && str[i]<='f' || str[i]>='A' && str[i]<='F' ){
            ret[j] = str[i];
            j++;
        }
    }
    //从个位数(即数组高位)开始转换
    for ( i=j-1; i>=0; i-- ){
        if ( ret[i]>='0' && ret[i]<='9' ) result += factor * (ret[i]-'0');
        else if ( ret[i]>='a' && ret[i]<='f' ) result += factor * (ret[i]-'a'+10);
        else result += factor * (ret[i]-'A'+10);
        factor *= 16;
    }
	//判断符号,输出
    if ( index1>-1 ){
        if ( index1<index2 ) printf("-%lld", result);
        else printf("%lld", result);
    }
    
    return 0;
}

实验7-3-10 删除重复字符

本题要求编写程序,将给定字符串去掉重复的字符后,按照字符ASCII码顺序从小到大排序后输出。输入是一个以回车结束的非空字符串(少于80个字符)。

CODE

**可以联系《C语言程序设计(第3版)》教材配套习题 第七章 的删除字符串中的空格
**这里是用一个数组,读入不同字符

#include <stdio.h>
#define N 81

int main()
{
    char str[N];
    int i, j, k=0;
    int index;
    int tmp;
    
    str[k] = getchar();
    while( str[k]!='\n' ){
        for ( i=0; i<k; i++ ){
            if ( str[k]==str[i] ){ //如果相同则不会有后面的k++,不同才会放进下一个数组元素中
                break;
            }
        }
        if ( k==i ) k++;
        str[k] = getchar();
    }
    //选择法排序
    for ( i=0; i<k-1; i++ ){
        index = i;
        for ( j=i+1; j<k; j++ ){
            if ( str[j]<str[index] ) index = j;
        }
        tmp = str[i];
        str[i] = str[index];
        str[index] = tmp;
    }
    //输出
    for ( i=0; i<k; i++ ){
        putchar(str[i]);
    }
    
    return 0;
}

实验8-1-9 输出学生成绩

本题要求编写程序,根据输入学生的成绩,统计并输出学生的平均成绩、最高成绩和最低成绩。建议使用动态内存分配来实现。

CODE

**这题就是想记录一下:动态分配内存

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

int main()
{
    int n;
    double *score;
    double sum, average, max, min;
    sum = average = max = min = 0;
    
    scanf("%d", &n);
    score = (double *)malloc(n * sizeof(double)); //动态分配内存
    for ( int i=0; i<n; i++ ){
        scanf("%lf", &score[i]);
        sum += score[i];
    }
    max = min = *score;
    for ( int i=1; i<n; i++ ){
        if ( score[i]>max ) max = score[i];
        else if ( score[i]<min ) min = score[i];
    }
    average = sum / n;
    
    printf("average = %.2f\n", average);
    printf("max = %.2f\n", max);
    printf("min = %.2f", min);
    
    return 0;
}

实验9-5 查找书籍

给定n本书的名称和定价,本题要求编写程序,查找并输出其中定价最高和最低的书的名称和定价。

输入:第一行给出正整数n(<10),随后给出n本书的信息。每本书在一行中给出书名,即长度不超过30的字符串,随后一行中给出正实数价格。题目保证没有同样价格的书。

输出:在一行中按照“价格, 书名”的格式先后输出价格最高和最低的书。价格保留2位小数。

CODE

**这题的关键还是在于,输入字符型变量前,如果有输入数值型变量,需要getchar()收一下换行!!!

#include <stdio.h>
#define N 11
#define M 32

typedef struct books{
    char name[M];
    double price;
}info;

int main()
{
    info b[N];
    int n, i;
    int max, min;
    
    scanf("%d", &n);
    getchar();  //----------------------(**)
    for ( i=0; i<n; i++ ){
        gets(b[i].name);
        scanf("%lf", &b[i].price);
        getchar();  //------------------(***)
    }
    max = min = 0;
    for ( i=1; i<n; i++ ){
        if ( b[i].price>b[max].price ) max = i;
        else if ( b[i].price<b[min].price ) min = i;
    }
    printf("%.2f, %s\n", b[max].price, b[max].name);
    printf("%.2f, %s\n", b[min].price, b[min].name);
    
    return 0;
}

实验9-10 平面向量加法

本题要求编写程序,计算两个二维平面向量的和向量。

输入:在一行中按照“x​1​​ y​1​​ x​2​​ y​2​​”的格式给出两个二维平面向量v​1​​=(x​1​​,y​1​​)和v​2​​=(x​2​​,y​2​​)的分量。

输入:在一行中按照(x, y)的格式输出和向量,坐标输出小数点后一位(注意不能输出−0.0)。

CODE

**使用 %.nf 来控制输出的小数位数,当后一位大于5时进位,不大于5(<+5)时不进位,即五舍六入

#include <stdio.h>

typedef struct vector{
    double x;
    double y;
}vec;

int main()
{
    vec v, v1, v2;
    
    scanf("%lf%lf%lf%lf", &v1.x, &v1.y, &v2.x, &v2.y);
    v.x = v1.x+v2.x;
    v.y = v1.y+v2.y;
    if ( v.x>=-0.05 && v.x<0 ) v.x = 0;
    if ( v.y>=-0.05 && v.y<0 ) v.y = 0;
    printf("(%.1f, %.1f)", v.x, v.y);
    
    return 0;
}

实验11-1-1 英文单词排序

本题要求编写程序,输入若干英文单词,对这些单词按长度从小到大排序后输出。如果长度相同,按照输入的顺序不变。

输入格式:输入为若干英文单词,每行一个,以#作为输入结束标志。其中英文单词总数不超过20个,英文单词为长度小于10的仅由小写英文字母组成的字符串。

输出格式:输出为排序后的结果,每个单词后面都额外输出一个空格。

CODE

/* 每个测试点都发生段错误(可能是数组越界、堆栈溢出等情况引起),自己测试的时候发现只能输入两个单词,然后程序运行了一会儿就退出了,即在输入程序段就出现问题 */

#include <stdio.h>
#include <string.h>
#define N 22
void sort(char *p[], int n); //选择法排序
int main()
{
    char *pword[N];
    int i = 0;
    int len = 0;
    
    scanf("%s", pword[0]);
    getchar();
    while( strcmp(pword[i],"#")!=0 ){
        i++;
        scanf("%s", pword[i]);
        getchar();
    }
    
    len = i;
    sort(pword,len);
    
    for ( i=0; i<len; i++ ){
        printf("%s ", pword[i]);
    }
    
    return 0;
}

void sort(char *p[], int n)
{
    int j, k, index;
    char *tmp;
    for ( j=0; j<n-1; j++ ){
        index = j;
        for ( k=j+1; k<n; k++ ){
            if ( strlen(p[k])<strlen(p[index]) ) index = k;
        }
        tmp = p[j];
        p[j] = p[index];
        p[index] = tmp;
    }
}

/* 因为字符指针数组没有指向确定的地址 → 用一个二维字符数组装输入的字符串,再让字符指针数组中的元素一一指向字符串即可解决 */

更改后的CODE 1

#include <stdio.h>
#include <string.h>
#define N 22
#define M 12
void sort(char *p[], int n); //这里省略
int main()
{
    char *pword[N], str[N][M];
    int i = 0;
    int len = 0;
    
    scanf("%s", str[0]);
    while( str[i][0]!='#' ){
        pword[i] = str[i];
        i++;
        scanf("%s", str[i]);
    }
    
    len = i;
    sort(pword,len);
    
    for ( i=0; i<len; i++ ){
        printf("%s ", pword[i]);
    }
    
    return 0;
}

更改后的CODE 2

/* 动态分配内存空间 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 22
#define M 12
void sort(char *p[], int n); //这里省略
int main()
{
    char *pword[N], str[M];
    int i = 0;
    int len = 0;
    
    scanf("%s", str);
    getchar();
    while( str[0]!='#' ){ //'#'只会放在第一个位置
        pword[i] = (char *)malloc((strlen(str)+1) * sizeof(char));  //动态分配存储单元,注意字符串长度加1放'\0'
        strcpy(pword[i],str);
        i++;
        scanf("%s", str);
    }
    
    len = i;
    sort(pword,len);
    
    for ( i=0; i<len; i++ ){
        printf("%s ", pword[i]);
    }
    
    return 0;
}

指针数组的内存分配小结
如果一开始没有初始化的话,指针数组元素没有指向(野指针),就要手动给个指向,

  1. 可以另外开个数组,让它指向这个数组;
  2. 也可以用动态分配,(若申请成功)动态分配返回的也是指针(指向所分配内存空间的起始地址的指针)

实验11-1-7 藏头诗

输入格式:输入为一首中文藏头诗,一共四句,每句一行。注意:一个汉字占两个字节。

输出格式:取出每句的第一个汉字并连接在一起形成一个字符串并输出。同时在末尾输入一个换行符。

CODE

/* 一个汉字占两个字节,连续输出两个%c%c就好了,也是很神奇o( ̄▽ ̄)o */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 4
#define M 20

int main()
{
    char *poem[N], str[M];
    int i;
    
    for ( i=0; i<N; i++ ){
        scanf("%s", str);
        poem[i] = (char *)malloc((strlen(str)+1) * sizeof(char));
        strcpy(poem[i],str);
    }
    
    for ( i=0; i<N; i++ ){
        printf("%c%c", poem[i][0], poem[i][1]);
    }
    printf("\n");
    
    return 0;
}

更多推荐

《C语言程序设计实验与习题指导(第3版)》题目集