c语言程序设计第五版课后答案谭浩强 第六章习题答案

第六章:利用数组处理批量数据

1. 用筛选法求100之内的素数

【答案解析】

素数:约数为1和该数本身的数字称为素数,即质数

筛选法:又称为筛法。先把N个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数。因为希腊人是把数写在涂腊的板上,每要划去一个数,就在上面记以小点,寻求质数的工作完毕后,这许多小点就像一个筛子,所以就把埃拉托斯特尼的方法叫做“埃拉托斯特尼筛”,简称“筛法”。(另一种解释是当时的数写在纸草上,每要划去一个数,就把这个数挖去,寻求质数的工作完毕后,这许多小洞就像一个筛子。)

【代码实现】

//用筛选法求100以内的素数
#include<stdio.h>
int main()
{
	int i, j, k = 0;

	// 将数组汇总每个元素设置为:1~100
	int	a[100];
	for (i = 0; i < 100; i++)
		a[i] = i+1;

	// 因为1不是素数,把a[0]用0标记
	// 最后一个位置数字是100,100不是素数,因此循环可以少循环一次
	a[0] = 0;
	for (i = 0; i < 99; i++)
	{
		// 用a[i]位置的数字去模i位置之后的所有数据
		// 如果能够整除则一定不是素数,该位置数据用0填充
		for (j = i + 1; j < 100; j++)
		{
			if (a[i] != 0 && a[j] != 0)
			{
				//把不是素数的都赋值为0
				if (a[j] % a[i] == 0)
					a[j] = 0;
			}
		}
	}

	printf(" 筛选法求出100以内的素数为:\n");
	for (i = 0; i < 100; i++)
	{
		//数组中不为0的数即为素数
		if (a[i] != 0)
			printf("%3d", a[i]);
	}

	printf("\n");
	return 0;
}

运行结果

2. 用选择法对10个整数排序

【答案解析】

选择排序原理:

总共两个循环,外循环控制选择的趟数,内循环控制具体选择的方式。

用maxPos标记区间中首元素位置,然后用后序元素依次与maxPos标记的元素进行比较,如果有元素大于maxPos位置的元素,用maxPos标记该元素的位置,直到区间的末尾。

该趟选择完成后,即找到该区间中最大元素,如果maxPos标记的最大元素不在区间末尾,用maxPos位置元素与区间末尾的元素进行交换。

继续新一趟选择,直到区间中剩余一个元素

【代码实现】

#include<stdio.h>
int main()
{
	int array[] = {2,8,3,9,5,7,1,4,0,6};
	int size = sizeof(array) / sizeof(array[0]);
	// 输出原数组
	printf("排序前数组中数据为:");
	for (int i = 0; i < size; ++i)
		printf("%d ", array[i]);
	printf("\n");

	// 选择排序过程:
	// 外循环控制选择的趟数,总共选择size-1趟,
	// 减1是因为最后一趟选择区间中剩余一个元素,该趟选择可以忽略
	for (int i = 0; i < size-1; ++i)
	{
		// 用maxPos标记[0, size-i)区间中最大元素
		// 在该趟选择没有开始前,默认认为0号位置就是最大元素
		int maxPos = 0;
		for (int j = 1; j < size - i; ++j)
		{
			// 遍历区间[0, size-i)中元素,如果有元素比maxPos位置元素大,maxPos记录该元素位置
			if (array[j] > array[maxPos])
				maxPos = j;
		}

		// 如果最大元素不在区间末尾时,将最大元素与区间末尾元素交换
		if (maxPos != size - i - 1)
		{
			int temp = array[maxPos];
			array[maxPos] = array[size - i - 1];
			array[size - i - 1] = temp;
		}
	}

	// 输出原数组
	printf("选择排序后数组中数据为:");
	for (int i = 0; i < size; ++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

【结果截屏】

优化:既然一趟选择能找到最大的元素,那么也可以找到最小的元素,因此在一趟中可以找到最小和最大两个元素,最小元素放在区间左侧,最大元素放在区间右侧,可以减少选择的趟数。

#include<stdio.h>
int main()
{
	int array[] = {2,8,3,9,5,7,1,4,0,6};
	int size = sizeof(array) / sizeof(array[0]);
	// 输出原数组
	printf("排序前数组中数据为:");
	for (int i = 0; i < size; ++i)
		printf("%d ", array[i]);
	printf("\n");

	
	int begin = 0, end = size - 1;
	// [begin, end]区间中进行选择
	while (begin < end)
	{
		int maxPos = begin;  // 标记区间中最大元素的位置
		int minPos = begin;  // 标记区间中最小元素的位置
		int index = begin + 1;
		while (index <= end)
		{
			if (array[index] > array[maxPos])
				maxPos = index;

			if (array[index] < array[minPos])
				minPos = index;
			++index;
		}

		// 如果最大元素不在区间末尾,则交换
		if (maxPos != end)
		{
			int temp = array[maxPos];
			array[maxPos] = array[end];
			array[end] = temp;
		}

		// 如果在交换前区间末尾刚好存储的是最小的元素,则最小的元素被交换到maxPos位置
		// 此时需要更新minPos
		if (minPos == end)
			minPos = maxPos;

		// 如果最小元素不在区间起始位置,则交换
		if (minPos != begin)
		{
			int temp = array[minPos];
			array[minPos] = array[begin];
			array[begin] = temp;
		}

		// 最大与最小元素已经在区间的起始和末尾的位置,
		// 因此begin往后移动,end往前移动
		begin++;
		end--;
	}

	// 输出原数组
	printf("选择排序后数组中数据为:");
	for (int i = 0; i < size; ++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

3. 求一个3 X 3的整形矩阵对角线元素之和

【答案解析】

矩阵:即二维数组,矩阵行和列相等的二维数组称为方阵。

1 2 3

4 5 6

7 8 9

左上角到右下角对角线上数字:行下标和列下标相等

右上角到左下角对角线上数字:列下标减1 行下标加一

通过两个循环来取到对角线上的元素,并对其求和即可。

【代码实现】

#include<stdio.h>
int main()
{
	int array[3][3];
	int sumLT2RB = 0;  // 标记左上角到右下角对角线元素之和
	int sumRT2LB = 0;  // 标记右上角到左下角对角线元素之和
	printf("请输入3行3列的矩阵:\n");
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
			scanf("%d", &array[i][j]);
	}

	// 左上角到右下角对角线
	for (int i = 0; i < 3; ++i)
		sumLT2RB += array[i][i];

	for (int i = 0, j = 2; i < 3; ++i, j--)
		sumRT2LB += array[i][j];

	printf("左上角到右下角对角线元素之和: %d\n", sumLT2RB);
	printf("右上角到左下角对角线元素之和: %d\n", sumRT2LB);
	return 0;
}

【结果截屏】

4. 有一个已经排好序的数组,要求输入一个数后,按原来顺序的规律将它插入数组中

【答案解析】

将数据插入到已排序的序列中,只需两步即可:

  1. 找待插入元素在数组中的插入位置
  2. 插入元素

具体如下:

注意:极端情况下,end可能会被减为-1,比如在上述序列中插入0

【代码实现】

#include<stdio.h>
int main()
{
	int num = 0;
	int array[10] = {1,2,3,4,5,6,7,8,9};

	printf("请输入一个整数:");
	scanf("%d", &num);

	printf("原数组序列为:\n");
	for (int i = 0; i < 9; ++i)
		printf("%d ", array[i]);
	printf("\n");

    // 数组为升序
    // 在数组中找待插入元素的位置,具体找的方式为:
    // 从后往前依次与数组中元素进行比较,如果要插入元素num比end位置数据小,则num一定插在end位置之前
    // 因此将end位置数据往后搬移一个位置
    // 如果num大于end位置元素或者end已经在区间最左侧,则位置找到
    // 最后将新元素插入到end+1的位置
	int end = 8;
	while (end >= 0 && num < array[end])
	{
		array[end+1] = array[end];
		end--;
	}

	array[end + 1] = num;
	printf("插入元素%d之后的结果为:\n", num);
	for (int i = 0; i < 10; ++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

【结果截图】

5. 将一个数组中的值按逆序重新存放。例如:原来顺序为8,6,5,4,1。要求改为1,4,5,6,8。

【答案解析】

该题为数组的逆置,具体处理方式如下:

如果begin < end时,则循环进行一下操作

  1. 给定两个下标begin和end,begin放在数组起始的位置,end放在数组最后一个元素的位置
  2. 交换begin和end位置的元素
  3. begin往后移动,end往前移动

【代码实现】

#include<stdio.h>
int main()
{
	int array[5] = {8,6,5,4,1};
	int begin = 0, end = 4;

	printf("逆序之前数组为:");
	for (int i = 0; i < 5; ++i)
		printf("%d ", array[i]);
	printf("\n");

	// 逆序:begin在数组最左侧,end在数组最右侧
	// 只要begin < end,将begin和end位置元素进行交换
	// 然后begin往后移动一步,end往前移动一步
	while (begin < end)
	{
		int temp = array[begin];
		array[begin] = array[end];
		array[end] = temp;
		begin++;
		end--;
	}

	printf("逆置之后数组为:");
	for (int i = 0; i < 5; ++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

【结果截屏】

6. 输出一下的杨慧三角(要求输出10行)

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

【答案解析】

仔细观察杨慧三角可以看到:

第0列和对角线上的数据全部为1,其余位置上的数据为上一行正对数据与上一行正对前一个数据之和。

比如:a[4][2] = a[3][2] + a[3][1]

【代码实现】

#include<stdio.h>
int main()
{
	int array[10][10];
	for (int i = 0; i < 10; ++i)
	{
		for (int j = 0; j <= i; ++j)
		{
            // 对角线和第0列上全部为1
			if (i == j || 0 == j)
				array[i][j] = 1;
			else
				array[i][j] = array[i - 1][j] + array[i - 1][j - 1];
		}
	}

    // 打印杨慧三角的前10行
	for (int i = 0; i < 10; ++i)
	{
		for (int j = 0; j <= i; ++j)
		{
			printf("%5d", array[i][j]);
		}

		printf("\n");
	}
	return 0;
}

【结果截屏】

7. 输出"魔方阵"。所谓魔方阵是指这样的方阵,它的每一行、每一列和对角线之和均相等。例如:

8   1   6
3   5   7
4   9   2

要求输出1~$n^2$的自然数构成的魔方阵。

【答案解析】

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

仔细观察上述矩阵,可以看到以下规律:

魔方阵的生成方法为:在第0行中间置1,对从2开始的其余$n^2-1$个数依次按下列规则存放:

  1. 将1放在第1行的中间一列。
  2. 从2开始直到n*n止,各数依次按此规律存放:每一个数存放的行比前一个数的行数减1,列数加1。
  3. 如果上一行的行数为1,则下一个数的行数为n(指最下一行)。
  4. 当上一个数的列数为n时,下一个数的列数应该为1。
  5. 如果按上面规律确定的位置有数,或者上一个数是第1行第n列时,则把下一个数放在上一个数的下面。

【代码实现】

#include <stdio.h>
int main()
{
	int a[15][15], n, i, j, k;
	while (1)
	{
		printf("请输入n(1~15):");
		scanf("%d", &n);
		if (n != 0 && n <= 15 && n % 2 != 0)
			break;
		else
		{
			printf("请输入奇数\n");
		}
	}

	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
			a[i][j] = 0;
	}

	j = n / 2 + 1;
	a[1][j] = 1;
	i = 1;
	for (k = 2; k <= n*n; k++)
	{
		i -= 1;
		j += 1;

		if (i<1 && j>n)
		{
			i += 2;
			j -= 1;
		}
		else if (i<1)
		{
			i = n;
		}
		else if (j>n)
		{
			j = 1;
		}

		if (a[i][j] == 0)
		{
			a[i][j] = k;
		}
		else
		{
			i += 2;
			j -= 1;

			a[i][j] = k;
		}
	}

	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
			printf("%5d", a[i][j]);

		printf("\n");
	}

	return 0;
}

【结果截屏】

8. 找出一个二维数组中的鞍点,即该位置上的元素在该行上最大,在该列上最小,也可能没有鞍点。

【答案解析】

鞍点是行上最大,列上最小的元素,因此对数组的第i元素进行如下操作:

  1. 找到该行上最大元素,用max标记,并标记该元素所在列colindex
  2. 找colindex列上最小的元素,用min标记,并标记该元素所在行号rowindex
  3. 如果max和min相等,并且最小的元素刚好是在第i行,则为鞍点

如果所有行找完了,没有输出则没有鞍点

【代码实现】

#include<stdio.h>
#define M 3
#define N 4

int main()
{
	int max, min, rowindex, colindex, flag = 0;
	int array[M][N];
	printf("请输入%d行%d列的数组:\n", M, N);
	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < N; j++)
			scanf("%d", &array[i][j]);
	}

	for (int i = 0; i < M; ++i)
	{
		// 找到i行上最大的元素,记录该元素在列号colindex
		max = array[i][0];
		for (int j = 0; j < N; ++j)
		{
			if (array[i][j] > max)
			{
				max = array[i][j];
				colindex = j;
			}
		}

		// 找max所在列colindex上最小的元素,并记录其所在的行
		min = array[0][colindex];
		for (int j = 0; j < M; ++j)
		{
			if (array[j][colindex] < min)
			{
				min = array[j][colindex];
				rowindex = j;
			}
		}

		// 如果最小元素与最小元素相同,并且最小元素也在第i行,则为鞍点
		if (max == min && i == rowindex)
		{
			flag = 1;
			printf("鞍点为:%d行%d列的元素%d", rowindex, colindex, max);
			break;
		}
	}

	if (0 == flag)
		printf("没有鞍点");
	return 0;
}

【结果截屏】

9. 有15个数按由大到小顺序存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。如果该数不在数组中,则输出"无此数"。

【答案解析】

二分查找是一个非常高效简单的查找算法,笔试和面试中非常喜欢考察。

折半查找又叫二分查找,查找的前提是序列中元素必须有序,假设区间使用[left, right)标记,待查找元素为key,具体查找的方式如下:当区间[left, right)有效时循环进行一下操作

  1. 找到[left, right)区间中间位置
  2. 如果key等于中间位置元素,则找到,返回该元素在数组中的下标
  3. 如果key小于中间位置元素,到数组的左半侧继续二分查找
  4. 如果key大于中间位置元素,到数组的右半侧继续二分查找

如果循环结束时还没有找到,则不存在该元素。

【代码实现】

#include<stdio.h>
int main()
{
	int array[15] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
	int left = 0;
	int right = sizeof(array) / sizeof(array[0]);
	int key = 0;

	printf("请输入要查找的数字: ");
	scanf("%d", &key);

	// 二分查找
	while (left < right)
	{
		// 找到中间位置
		int mid = left + ((right - left) >> 1);
		if (key == array[mid])
		{
			printf("%d\n", mid);
			break;
		}
		else if (key < array[mid])
		{
			right = mid;
		}
		else
		{
			left = mid + 1;
		}
	}

	if (left >= right)
		printf("无此数\n");
	return 0;
}

【结果截屏】

假设数组中元素为:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

10. 有一篇文章,共有3行文字,每行有80个字符。要求分别统计出其中英文大写字母、小写字母、数字、空格以及其他字符的个数。

【答案解析】

获取文章中的3行文本,并对每行文本进行以下操作

  1. 定义保存结果变量:upp、low、digit、space、other
  2. 遍历每行文本中的字符
  3. 如果该字符ch:ch >= 'a' && ch <='z',则该字符是小写字母,给low++
  4. 如果该字符ch:ch >= 'A' && ch <='Z',则该字符是小写字母,给up++
  5. 如果该字符ch:ch >= '0' && ch <='9',则该字符是小写字母,给digit++
  6. 如果该字符ch:ch == ' ',则该字符是小写字母,给space++
  7. 否则为其他字符,给other++

输入统计结果

【代码实现】

#include <stdio.h>
int main()
{
    int upp = 0, low = 0, digit = 0, space = 0, other = 0;
    char text[3][80];
    
    for (int i=0; i<3; i++)
    {
        // 获取一行文本
        printf("please input line %d:\n",i+1);
        gets(text[i]);
        
        // 统计该行文本中小写字母、大写字母、数字、空格、其他字符的个数
        for (int j=0; j<80 && text[i][j]!='\0'; j++)
        {
            if (text[i][j]>='A'&& text[i][j]<='Z')   // 大写字母
                upp++;
            else if (text[i][j]>='a' && text[i][j]<='z')  // 小写字母
                low++;
            else if (text[i][j]>='0' && text[i][j]<='9')  // 数字
                digit++;
            else if (text[i][j]==' ')  // 控制
                space++;
            else
                other++;   // 其他字符
        }
     }
    
     printf("\nupper case: %d\n", upp);
     printf("lower case: %d\n", low);
     printf("digit     : %d\n", digit);
     printf("space     : %d\n", space);
     printf("other     : %d\n", other);
 
    return 0;
}

【结果截屏】

11. 输出一下图案:

* * * *
  * * * *
    * * * *
      * * * *
        * * * *

【答案解析】

该题非常简单,观察图形可以看出:

  1. 每行中*的个数相等,都是4个
  2. 每行中空格的个数在递增2个
  3. 每行先输出空格,然后输出*

按照以上方式通过循环来控制每行的输出内容即可

【代码实现】

#include<stdio.h>
int main()
{
	for (int i = 0; i < 5; ++i)
	{
		// 输出空格
		for (int j = 0; j < i; ++j)
			printf("  ");

		// 输出*
		printf("* * * *\n");
	}
	return 0;
}

【结果截屏】

12. 有一行电文,以按下面规律译成密码:

A--->Z   a--->z
B--->Y   b--->Y
C--->X   c--->x
……

即第1个字母编程第26个字母,第i个字母编程第(26-i+1)个字母,非字母字符不变,要求编程序将密码译回原文,并输出密码和原文。

【答案解析】

从题目给的实例中可以看到,编码规则非常简单,就是将从前往后数的第i个字母转化为从后往前数的第i个字母。

那解压时直接反过来转换即可:

即'Z'--->'A' 'z'--->'a'

​ 'Y'--->'B' 'y'--->'b'

​ 'X'--->'C' 'x'--->'c'

假设如果当前拿到的是小写字母,转换方式如下:

  1. 先用s[i] - 'a'计算出s[i]是26个字母中从前往后数的第几个
  2. 再用26 - (s[i]- 'a') - 1 转换为26个字母中从后往前数的第几个
  3. 在2的结果上加上'a',即转换为对应从后往前的第几个字母

大写字母转换方式与上述相同,将上述每条中的'a'换为‘A’即可。

【代码实现】

#include<stdio.h>
int main()
{
	char s[1024] = {0};
	scanf("%s", s);

	int len = strlen(s);
	// 转换
	for (int i = 0; i < len; ++i)
	{
		// 如果是小写字母(大写字母出来类似):
		// 1. 先用s[i] - 'a'计算出s[i]是26个字母中从前往后数的第几个
		// 2. 再用26 - (s[i]- 'a') - 1 转换为26个字母中从后往前数的第几个
		// 3. 在2的结果上加上'a',即转换为对应从后往前的第几个字母
		if (s[i] >= 'a' && s[i] <= 'z')
			s[i] = 'a' + 26 - (s[i]-'a')-1;
		else if (s[i] >= 'A' && s[i] <= 'Z')
			s[i] = 'A' + 26 - (s[i] - 'A')-1;
	}

	printf("%s", s);
	return 0;
}

【结果截屏】

13. 编一程序,将两个字符串连接起来,不要用strcat函数

【答案解析】

直接将s2中的字符逐个拷贝到s1的末尾即可,用户需要保证s1中能存的下s2中的字符

  1. 获取s1末尾的位置
  2. 将s2中的字符逐个拷贝到s1中

【代码实现】

#include<stdio.h>

int main()
{
	char s1[100] = {0};
	char s2[50] = { 0 };
	int index1 = 0, index2 = 0;
	printf("请输入字符串s1:");
	scanf("%s", s1);

	printf("请输入字符串s2:");
	scanf("%s", s2);

	printf("将s2拼接在s1之后: ");
	// 1. 找到s1的末尾
	while ('\0' != s1[index1])
		index1++;

	// 2. 将s2中的字符逐个往s1之后拼接
	while (s1[index1++] = s2[index2++]);

	printf("%s\n", s1);
	return 0;
}

【结果截屏】

14. 编写一个程序,将连个字符串s1和s2比较,如果s1 > s2,输出一个整数;若s1 = s2,输出0;若s1 < s2,输出一个负数。不要用strcpy函数。两个字符串用gets函数读入。输出的正数或负数的绝对值应是相比较的两个字符串相对应字符的ASCII码的差值。例如,"A"和“C”相比,由于"A" < "C",应输出负数,同时由于‘A’与‘C’的ASCII码差值为2,因此应输出"-2"。同理:“And”和"Aid"相比较,根据第2个字符比较结果,"n"比"i"大5,因此应输出"5"。

【答案解析】

字符串比较规则:从前往后逐个字符进行比较,相等时继续往后,不相等时返回该位置两个字符差值。

【代码实现】

#include <stdio.h>

int main()
{
	int ret = 0;
	int index = 0;
	char s1[100] = { 0 };
	char s2[100] = { 0 };
	printf("请输入s1:");
	gets(s1);

	printf("请输入s2:");
	gets(s2);

    // 将s1和s2中的字符从前往后逐个进行比较,相等继续往后,
    // 不相等时ret中结果不为0,!ret则为0  循环结束
    // 如果一个走到末尾,一个未走到末尾 ret也不为0, !ret为0,循环结束
    // 如果两个字符串相等,同时达到末尾,循环结束
	while (!(ret = s1[index] - s2[index]) && '\0' != s1[index] && '\0' != s2[index])
	{
		++index;
	}

	printf("%d\n", ret);
	return 0;
}

【结果截屏】

15. 编写一个程序,将字符数组s2中的全部字符复制到字符数组s1中,不用strcpy函数。复制时,‘\0’也要赋值过去。'\0'之后的字符不复制。

【答案解析】

首先必须保证s1能否放的下s2中的字符,然后将s2中的每个字符逐个搬移到s1中即可。

【代码实现】

#include<stdio.h>

int main()
{
	char s1[100] = { 0 };
	char s2[50] = { 0 };
	int index1 = 0, index2 = 0;
	printf("请输入字符串s2:");
	scanf("%s", s2);

	printf("将s2拷贝到s1中, s1现在为: ");
    
    // 将s2[index2]位置字符拷贝到s1[index]位置,
    // 然后以s1[index1]的值作为循环条件判断是否拷贝到s2的末尾
	while (s1[index1++] = s2[index2++]);
	printf("%s\n", s1);
	return 0;
}

【结果截屏】

c语言程序设计第五版课后答案谭浩强更多答案

c语言程序设计第五版课后答案谭浩强 第五章课后答案
c语言程序设计第五版课后答案谭浩强 第七章课后答案

posted @ 2020-06-30 16:19  chenke21  阅读(9130)  评论(0编辑  收藏  举报