C语言程序设计 现代设计方法_第9章代码、练习题及编程题答案

9.1 average.c
程序  计算平均值
假设我们经常需要计算两个double 类型数值的平均值。C语言库没有“求平均值”函数,但是可以自己定义一个。下面就是这个函数的形式:

double average(double a, double b)
{
   
	return (a + b) / 2;
}

在函数开始处放置的单词double 表示average 函数的返回类型(return type),也就是每次调用该函数时返回数据的类型。 标识符a 和标识符b (即函数的形式参数 (parameter))表示在调用average 函数时需要提供的两个数。每一个形式参数都必须有类型(正像每个变量有类型一样),这里选择了double 作为a 和b 的类型。(这看上去有点奇怪,但是单词double 必须出现两次,一次为a 而另一次为b 。)函数的形式参数本质上是变量,其初始值在调用函数的时候才提供
每个函数都有一个用花括号括起来的执行部分,称为函数体(body)。average 函数的函数体由一条return 语句构成。执行这条语句将会使函数“返回”到调用它的地方,表达式(a+b)/2 的值将作为函数的返回值。
**为了调用函数,需要写出函数名及跟随其后的实际参数(argument)列表。**例如,average(x, y) 是对average 函数的调用。实际参数用来给函数提供信息;在此例中,函数average 需要知道是要求哪两个数的平均值。调用average(x, y) 的效果就是把变量x 和y 的值复制给形式参数a 和b ,然后执行average 函数的函数体。实际参数不一定要是变量,任何正确类型的表达式都可以,average(5.1, 8.9) 和average(x/2, y/3) 都是合法的函数调用。
我们把average 函数的调用放在需要使用其返回值的地方。例如,为了计算并显示出x 和y 的平均值,可以写成
printf(“Average: %g\n”, average(x, y));
这条语句产生如下效果。
(1) 以变量x 和y 作为实际参数调用average 函数。
(2) 把x 和y 的值复制给a 和b 。
(3) average 函数执行自己的return 语句,返回a 和b 的平均值。
(4) printf 函数显示出函数average 的返回值。(average 函数的返回值成为了函数printf 的一个实际参数。)
注意,我们没有保存average 函数的返回值,程序显示这个值后就把它丢弃了。如果需要在稍后的程序中用到返回值,可以把这个返回值赋值给变量:
avg = average(x, y);
这条语句调用了average 函数,然后把它的返回值存储在变量avg中。
现在把average 函数放在一个完整的程序中来使用。下面的程序读取了3个数并且计算它们的平均值,每次计算一对数的平均值:
Enter three numbers: 3.5 9.6 10.2
Average of 3.5 and 9.6: 6.55
Average of 9.6 and 10.2: 9.9
Average of 3.5 and 10.2: 6.85
这个程序表明只要需要可以频繁调用函数。

/* average.c (Chapter 9, page 185) */
/* Computes pairwise averages of three numbers */

#include <stdio.h>

double average(double a, double b)
{
   
  return (a + b) / 2;
}

int main(void)
{
   
  double x, y, z;

  printf("Enter three numbers: ");
  scanf("%lf%lf%lf", &x, &y, &z);
  printf("Average of %g and %g: %g\n", x, y, average(x, y));
  printf("Average of %g and %g: %g\n", y, z, average(y, z));
  printf("Average of %g and %g: %g\n", x, z, average(x, z));

  return 0;
}

9.1 countdown.c
程序  显示倒计数
不是每个函数都返回一个值。例如,进行输出操作的函数可能不需要返回任何值。为了指示出不带返回值的函数,需要指明这类函数的返回类型是void 。(void 是一种没有值的类型。)思考下面的函数,这个函数用来显示信息T minus and counting ,其中 的值在调用函数时提供:
void print_count(int n)
{
printf(“T minus %d and counting\n”, n);
}
函数print_count 有一个形式参数n ,参数的类型为int 。此函数没有返回任何值,所以用void 指明它的返回值类型,并且略掉了return 语句。既然print_count 函数没有返回值,那么不能使用调用average 函数的方法来调用它。print_count 函数的调用必须自成一个语句:
print_count(i);
下面这个程序在循环内调用了10次print_count 函数.

/* countdown.c (Chapter 9, page 186) */
/* Prints a countdown */

#include <stdio.h>

void print_count(int n)
{
   
  printf("T minus %d and counting\n", n);
}

int main(void)
{
   
  int i;

  for (i = 10; i > 0; --i)
    print_count(i);

  return 0;
}

9.1 pun2.c
程序  显示双关语(改进版)
有些函数根本没有形式参数。思考下面这个print_pun 函数,它在每次调用时显示一条双关语:

void print_pun(void)
{
   
	printf("To C, or not to C: that is the question.\n");
}

在圆括号中的单词void 表明print_pun 函数没有实际参数。(这里使用void 作为占位符,表示“这里没有任何东西”。)调用不带实际参数的函数时,只需要写出函数名并且在后面加上一对圆括号:
print_pun();
即使没有实际参数也必须 给出圆括号。下面这个小程序测试了print_pun 函数:

/* Prints a bad pun */
#include <stdio.h>
void print_pun(void)
{
   
	printf("To C, or not to C: that is the question.\n");
}
int main(void)
{
   
	print_pun();
	return 0;
}

9.1 prime.c
程序  判定素数
为了弄清楚函数如何使程序变得更加容易理解,现在来编写一个程序用以检查一个数是否是素数。这个程序将提示用户录入数,然后给出一条消息说明此数是否是素数:
Enter a number: 34
Not prime
我们没有在main 函数中加入素数判定的细节,而是另外定义了一个函数,此函数返回值为true 就表示它的形式参数是素数,返回false 就表示它的形式参数不是素数。给定数n 后,is_prime 函数把n 除以从2到n 的平方根之间的每一个数;只要有一个余数为0,n 就不是素数。

/* prime.c (Chapter 9, page 190) */
/* Tests whether a number is prime */

#include <stdbool.h>   /* C99 only */
#include <stdio.h>

bool is_prime(int n)
{
   
  int divisor;

  if (n <= 1)
    return false;
  for (divisor = 2; divisor * divisor <= n; divisor++)
    if (n % divisor == 0)
      return false;
  return true;
}

int main(void)
{
   
  int n;

  printf("Enter a number: ");
  scanf("%d", &n);
  if (is_prime(n))
    printf("Prime\n");
  else
    printf("Not prime\n");

  return 0;
}

注意,main 函数包含一个名为n 的变量,而is_prime 函数的形式参数也叫n 。一般来说,在一个函数中可以声明与另一个函数中的变量同名的变量。这两个变量在内存中的地址不同,所以给其中一个变量赋新值不会影响另一个变量。(形式参数也具有这一性质。)10.1节会更详细地讨论这个问题。
如is_prime 函数所示,函数可以有多条return 语句。但是,在任何一次函数调用中只能执行其中一条return 语句,这是因为到达return 语句后函数就会返回到调用点。在9.4节我们会更深入地学习return 语句。

9.6 快速排序算法

实际上,递归经常作为分治法 (divide-and-conquer)的结果自然地出现。这种称为分治法的算法设计方法把一个大问题划分成多个较小的问题,然后采用相同的算法分别解决这些小问题。分治法的经典示例就是流行的排序算法——快速排序 (quicksort)。快速排序算法的操作如下(为了简化,假设要排序的数组的下标从1到n)。
(1) 选择数组元素 e(作为“分割元素”),然后重新排列数组使得元素从1一直到i-1都是小于或等e 的,元素i包含e,而元素从i+1一直到n都是大于或等于e的。
(2) 通过递归地采用快速排序方法,对从1到i-1的元素进行排序。
(3) 通过递归地采用快速排序方法,对从i+1到n的元素进行排序。
执行完第1步后,元素e处在正确的位置上。因为e左侧的元素全部都是小于或等于e的,所以第2步对这些元素进行排序,那么这些小于或等于e的元素也将会处在正确的位置上。类似的理由也可以应用于e右侧的元素。

显然快速排序中的第1步是很关键的。有许多种方法可以用来分割数组,有些方法比其他的方法好很多。下面将采用的方法是很容易理解
的,但是它不是特别高效。下面首先将概括地描述分割算法,稍后会把这种算法翻译成C代码。
该算法依赖于两个命名为low和high的标记,这两个标记用来跟踪数组内的位置。开始, low指向数组中的第一个元素,而high 指向末尾元素。首先把第一个元素(分割元素)复制给其他地方的一个临时存储单元,从而在数组中留出一个“空位”。接下来,从右向左移动,直到 指向小于分割元素的数时停止。然后把这个数复制给指向的空位,这将产生一个新的空位(high 指向的)。现在从左向右移动low ,寻找大于分割元素的数。在找到时,把这个找到的数复制给 low指向的空位。重复执行此过程,交替操作 low和 high直到两者在数组中间的某处相遇时停止。此时,两个标记都指向空位,只要把分割元素复制给空位就够了。下面的图演示了对整数数组进行快速排序的过程。
在这里插入图片描述
在这里插入图片描述
此时我们已经实现了目标:分割元素左侧的所有元素都小于或等于12,而其右侧的所有元素都大于或等于12。既然已经分割了数组,那
么可以使用快速排序法对数组的前4个元素(10、3、6和7)和后2个元素(15和18)进行递归快速排序了。

程序  快速排序算法
先来开发一个名为quicksort 的递归函数,此函数采用快速排序算法对数组元素进行排序。为了测试函数,将由main 函数往数组中读入10个元素,调用quicksort 函数对该数组进行排序,然后显示数组中的元素:
Enter 10 numbers to be sorted: 9 16 47 82 4 66 12 3 25 51
In sorted order: 3 4 9 12 16 25 47 51 66 82
因为分割数组的代码有一点长,所以把这部分代码放置在名为split的独立的函数中。

/* Sorts an array of integers using Quicksort algorithm */

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

#define N 10

int split(int a[]
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值