程序的基本概念,常量,变量,表达式

1、解释执行的语言相比编译执行的语言有什么优缺点?

由于少了编译过程,解释型语言开发调试的周期更短;由于不需要生成机器指令,解释型语言平台无关性更好;解释型语言的执行效率不如编译型语言,因为在运行时还要解释源代码或中间代码,而编译型语言的程序在运行时没有这个负担。

1、总结前面介绍的转义序列的规律,想想在printf的格式化字符串中怎么表示一个%字符?写个小程序试验一下。

printf("%%") 根据转义序列的\规则类比可知printf("%%");

假设变量x和n是两个正整数,我们知道 x/n 这个表达式的结果是取Floor,例 如x是17,n是4,则结果是4。如果希望结果取Ceiling应该怎么写表达式呢?例 如x是17,n是4,则结果是5,而x是16,n是4,则结果是4。

答案是(x+n-1)/n。 这题很重要,多给点时间让学生想。一个班差不多有一两个人能想出来。有些人自以为学得不错,不好好听课,就可以拿这题打击打击。有些人用if/else或三目运算符做出来,应该discourage他们。这种写法以后很常见,绝非奇技淫巧,比如以后讲到对齐会见到这样的情况,分配的内存必须是4字节的整数倍。 给学生解释可以这么说:先拆成x/n+(n-1)/n来看,如果x/n还能余出一点儿来,合写成(x+n-1)/n就可以把余出来的那一点儿给(n-1),从而使(n-1)/n等于1。

简单函数

如果在一个程序中调用了printf函数却不包含头文件,例如int main(void) { printf("\n"); },编译时会报警告:warning: incompatible implicit declaration of built-in function ‘printf’。请分析错误原因。

没有包含头文件就没有printf函数的声明,编译器要做隐式声明,返回值int,参数一个,而真正的printf原型返回值是int,参数是可变个数的,所以incompatible。

定义变量时可以把同样类型的变量列在一起,而定义参数却不可以。其实这里的这个规定也不算十分的例外,并不是C语言的设计者故意找茬,而是不得不这么规定,读者想想为什么 呢?

假如使用该规则, 当有多个类型的变量即int add(int a, b, double c, d); 难以检查语法正确性.

分支语句

1、以下程序段编译能通过,执行也不出错,但是执行结果不正确(根据第 3 节 “程序的调试”的定义,这是一个语义错误),请分析一下哪里错了。还有,既然错了为什么编译能通过呢?

int x = -1;
if (x > 0);
    printf("x is positive.\n");

if判断后有个分号;(空语句), 因为这不是语法错误,所以能够编译通过。

1、写两个表达式,分别取整型变量x的个位和十位。

1) x%10 2)x%100/10

2、写一个函数,参数是整型变量x,功能是打印x的个位和十位。

void print_x(int x) {
    printf("个位: %d 十位: %d \n", x%10, x%100/10);
}

1、把代码段 if (x > 0 && x < 10); else printf("x is out of range.\n"); 改写成下面这种形式:

if (____ || ____)
    printf("x is out of range.\n");

__应该怎么填?

x<=0      x>=10
德摩根律

2、把代码段:

if (x > 0)
    printf("Test OK!\n");
else if (x <= 0 && y > 0)
    printf("Test OK!\n");
else
    printf("Test failed!\n");

改写成下面这种形式: if ( && ) printf("Test failed!\n"); else printf("Test OK!\n"); __应该怎么填?

(不确定是否最优) y<=0 x<=0 x+¬x*y=x+y,德摩根律

3、有这样一段代码:

if (x > 1 && y != 1) {
    ...
} else if (x < 1 && y != 1) {
    ...
} else {
    ...
}

要进入最后一个else,x和y需要满足条件 || 。这里应该怎么填?

x==1 y==1 x(y+z)=xy+x*z,德摩根律

4、以下哪一个if判断条件是多余的可以去掉?这里所谓的“多余”是指,某种情况下如果本来应该打印Test OK!,去掉这个多余条件后仍然打印Test OK!,如果本来应该打印Test failed!,去掉这个多余条件后仍然打印Test failed!。

if (x<3 && y>3)
    printf("Test OK!\n");
else if (x>=3 && y>=3)
    printf("Test OK!\n");
else if (z>3 && x>=3)
    printf("Test OK!\n");
else if (z<=3 && y>=3)
    printf("Test OK!\n");
else
    printf("Test failed!\n");

第四个条件else if (z<=3 && y>=3) 可以去掉。 因为在第二个判断假如不符合条件,可以确定y一定小于3。那么而第三个条件不符合,可以得到z>3,换言之,第四个条件一定为假,没有必要。 xy+¬xz+yz=xy+¬x*z

深入理解函数

1、编写一个布尔函数int is_leap_year(int year),判断参数year是不是闰年。如果某年份能被4整除,但不能被100整除,那么这一年就是闰年,此外,能被400整除的年份也是闰年。

code

2、编写一个函数double myround(double x),输入一个小数,将它四舍五入。例如myround(-3.51)的值是-4.0,myround(4.49)的值是4.0。可以调用math.h中的库函数ceil和floor实现这个函数。

code

在上面的例子中,如果循环结束条件就要写成 i<n ,还要结果正确,那么前面应该怎么改?

    n += 1;

2、在上一节中讲过怎样把 for 语句写成等价的 while 语句,但也提到如果循环体中有 continue 语句,这两种形式就不等价了,想一想为什么不等价了?

因为for里有执行语句,当continue后会执行(比如++i),而while里则没有。

1、编写递归函数求两个正整数a和b的最大公约数(GCD,Greatest Common Divisor),使用Euclid算法:

  1. 如果a除以b能整除,则最大公约数是b。
  2. 否则,最大公约数等于b和a%b的最大公约数。 Euclid算法是很容易证明的,请读者自己证明一下为什么这么算就能算出最大公约数。
int GCD(int a, int b) {
if(a%b == 0)
    return b;
else
    return GCD(b, a%b);
}

2、编写递归函数求Fibonacci数列的第n项,这个数列是这样定义的: fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2) 这个递归函数做两次递归调用,会形成树状的调用关系,需要画图解释。

int Fibonacci(int n) {
if(n==0 || n==1)
    return 1;
else
    return Fibonacci(n-1) + Fibonacci(n-2);
}

循环语句

1、用循环解决第 3 节 “递归”的所有习题,体会递归和循环这两种不同的思路。

int w_GCD(int a, int b) {
    while(a%b != 0) {
        int temp = a;
        a = b;
        b = temp%b;
    }
    return b;
}

int w_Fibonacci(int n) {
    int i, sum=1, before=1;
    for(i=1; i<n; ++i) {
        sum += before;
        before = sum;
    }
    return sum;
}

2、编写程序数一下1到100的所有整数中出现多少次数字9。在写程序之前先把这些问题考虑清楚:

  1. 这个问题中的循环变量是什么?
  2. 这个问题中的累加器是什么?用加法还是用乘法累积?
  3. 在第 2 节 “if/else语句”的习题1写过取一个整数的个位和十位的表达式,这两个表达式怎样用到程序中?

1.数字 1-100 2.i,加法累加

int count(void) {
    int i, count=1;
    for(i=10; i<=100; ++i) {
        if(i/9==0 || i%10 == 9) {
            ++count;
        }
    }
    return count;
}

其实没有必要从2一直检查到n-1,只要从2检查到⌊sqrt(n)⌋,如果全都不能整除就足以证明n是素数了,解释一下为什么

对称性.

求素数这个程序只是为了说明break和continue的用法才这么写的,其实完全可以不用break和continue,请读者修改一下循环的结构,去掉break和continue而保持功能不变

#include <stdio.h>
int is_prime(int n)
{
    int i;
    for (i = 2; i < n; i++)
        if (n % i == 0)
            return 0;
        return 1;
}
int main(void)
{
    int i;
    for (i = 1; i <= 100; i++) {
        if (is_prime(i))
            printf("%d\n", i);
}
return 0;
}

1、上面打印的小九九有一半数据是重复的,因为89和98的结果一样。请修改程序打印这样的小九九: 1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
8 16 24 32 40 48 56 64
9 18 27 36 45 54 63 72 81

void print_numbers(void)
{
    int i, j;
    for(i=1; i<10; ++i) {
        for(j=1; j<=i; ++j) {
            printf("%d\t", i*j);
        }
        printf("\n");
    }
}

2、编写函数diamond打印一个菱形。如果调用diamond(3, '*')则打印:

    *
*    *    *
    *

如果调用diamond(5, '+')则打印:

        +
    +    +    +
+    +    +    +    +
    +    +    +
        +

如果用偶数做参数则打印错误提示。

void diamod(int num, char graph)
{
    if(! num&1) {// %2 == 0
        printf("wrong\n");
        return;
    }
    int i, j, count;
    count = -1;
    for(i=num/2; i>=0; --i) {
        count += 2;
        for(j=i; j>0; --j)
            printf("\t");
        for(j=count; j>0; --j)
            printf("%c\t", graph);
        printf("\n");
    }

    for(i=num/2+1; i<num; ++i) {
        for(j=i-num/2; j>0; --j)
            printf("\t");
        for(j=count-2; j>0; --j)
            printf("%c\t", graph);
        printf("\n");
        count -= 2;
    }
}
/* 宋老师答案 */
void diamond(int n, char c)
{
    int i, j;
    if (n <= 0 || !(n % 2))
        return;
    for (i = 1; i <= n/2+1; i++) {
        for (j = 1; j <= n/2-i+1; j++)
            printf("\t");
        for (j = 1; j <= 2*i-1; j++)
            printf("%c\t", c);
        printf("\n");
    }
    for (i = 1; i <= n/2; i++) {
        for (j = 1; j <= i; j++)
            printf("\t");
        for (j = 1; j <= n-2*i; j++)
            printf("%c\t", c);
        printf("\n");
    }
}

以下代码编译没有问题, 但运行结果却和预期不符合, 不能打印出other number, 请分析原因.

#include <stdio.h>
int main(void){
    int n = 3;
    switch(n) {
        case 1:
            printf("1\n");
            break;
        case 2:
            printf("2\n");
            break;
        default:
            printf("other number\n");
    }
    return 0;
}

好像这道题目出错了? 能够正常运行并且打印的.

结构体

1、在本节的基础上实现一个打印复数的函数,打印的格式是x+yi,如果实部或虚部为0则省略,例如:1.0、-2.0i、-1.0+2.0i、1.0-2.0i。最后编写一个main函数测试本节的所有代码。想一想这个打印函数应该属于上图中的哪一层?

存储表示层

    void print_complex(struct complex_struct z) {
        if(z.real()==0 && z.imag()==0)
            printf("0\n");
        else if(z.real() == 0)
            printf("%fi", z.imag());
        else if(z.imag() == 0)
            printf("%f", z.real());
        else
            printf("%f+%fi", z.real(), z.iamg());
    }

2、实现一个用分子分母的格式来表示有理数的结构体rational以及相关的函数,rational结构体之间可以做加减乘除运算,运算的结果仍然是rational 注意要约分为最简分数,例如1/8和-1/8相减的打印结果应该是1/4而不是2/8,可以利用第 3 节 “递归”练习题中的Euclid算法来约分。在动手编程之前先思考一下这个问题实现了什么样的数据抽象,抽象层应该由哪些函数组成。

struct Ragtional make_rational(int numerator, int denominator)
{
    struct Ragtional a;
    a.numerator = numerator;
    a.denominator = denominator;
    return a;
}
struct Ragtional add_rational(struct Ragtional a, struct Ragtional b)//need to fix -
{
    struct Ragtional result;
    int max, gcm;
    max = GCD(a.denominator, b.denominator);
    gcm = (a.denominator*b.denominator) / max;
    printf("gcm is %d\n", gcm);
    result.denominator = gcm;
    result.numerator = (a.numerator*gcm/a.denominator) + (b.numerator*gcm/b.denominator);
    printf("numerator is %d\n", result.numerator);
    return result;
}
struct Ragtional mul_rational(struct Ragtional a, struct Ragtional b)
{
    struct Ragtional result;
    result.numerator = a.numerator * b.numerator;
    result.denominator = a.denominator * b.denominator;
    return result;
}
struct Ragtional div_rational(struct Ragtional a, struct Ragtional b)
{
    struct Ragtional trans;
    trans.numerator =  b.denominator;
    trans.denominator = b.numerator;
    return mul_rational(a, trans);
}
void print_rational(struct Ragtional a)
{
    int max;
    max = GCD(a.numerator, a.denominator);
    if(a.denominator/max == 1)
        printf("%d\n", a.numerator/max);
    else
        printf("%d/%d\n", a.numerator/max, a.denominator/max);
}

1、本节只给出了make_from_real_img和make_from_mag_ang函数的实现,请读者自己实现real_part、img_part、magnitude、angle这些函数。

    double real_part(struct complex_struct z) {
        if(z.t==RECTANGULAR)
            return z.a;
        else
            return z.a*cos(z.b);
    }

    double imag_part(struct complex_struct z) {
        if(z.t==RECTANGULAR)
            return z.b;
        else
            return z.a*sin(z.b);
        }

    double magnitude(struct complex_struct z) {
        if(z.t==RECTANGULAR)
            return sqrt(z.a*z.a + z.b*z.b);
        else
            return z.a;
        }

    double angle(struct complex_struct z) {
        if(z.t==RECTANGULAR) {
            double PI = acos(-1.0);
            if(z.a > 0)
                return atan(z.b/z.a);
            else
                return atan(z.b/z.a) + PI;
            } else
                return z.b;
        }

2、编译运行下面这段程序:

#include <stdio.h>
enum coordinate_type { RECTANGULAR = 1, POLAR };
int main(void)
{
    int RECTANGULAR;
    printf("%d %d\n", RECTANGULAR, POLAR);
    return 0;
}

结果是什么?并解释一下为什么是这样的结果。

结果是 -1217339392 2, 原因是main函数中的未赋值RECTANGULAR变量覆盖了全局的enum变量。

数组

1、编写一个程序,定义两个类型和长度都相同的数组,将其中一个数组的所有元素拷贝给另一个。既然数组不能直接赋值,想想应该怎么实现。

    void copy(char *a, char *b) {
        int i;
        for(i=0; b[i]; ++i)
            a[i] = b[i];
    }

1、用rand函数生成10~20之间的随机整数,表达式应该怎么写?

rand()%11 + 10

1、补完本节直方图程序的main函数,以可视化的形式打印直方图。例如上一节统计20个随机数的结果是: 0 1 2 3 4 5 6 7 8 9



  *  *        *
              *
              *
    int j, sum;
    for(i=0; i<=9; i++)
        printf("%d ", i);
    printf("\n");
    for(i=0; i<=9; i++)
        printf("%d ", histogram[i]);
    printf("\n");
    do {
        sum = 0;
        for(i=0; i<=9; i++)
            sum += histogram[i];
        for(j=0; j<=9; ++j) {
            if(histogram[j]!=0) {
                printf("* ");
                --histogram[j];
            }else
                printf("  ");
        }
        printf("\n");
    } while(sum != 0);
    return 0;

2、定义一个数组,编程打印它的全排列。比如定义:

#define N 3

int a[N] = { 1, 2, 3 }; 则运行结果是: $ ./a.out 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 3 1 2 1 2 3

code

完成了上述要求之后再考虑第二个问题:如果再定义一个常量M表示从N个数中取几个数做排列(N == M时表示全排列),原来的程序应该怎么改? 最后再考虑第三个问题:如果要求从N个数中取M个数做组合而不是做排列,就不能用原来的递归过程了,想想组合的递归过程应该怎么描述,编程实现它。

code

留给读者思考的问题是:(man - computer + 4) % 3 - 1这个神奇的表达式是如何比较出0、1、2这三个数字在“剪刀石头布”意义上的大小的? 胜 负 平 胜 负 man-computer -2 -1 0 1 2 man-computer+4 2 3 4 5 6 (man-computer+4)%3 2 0 1 2 0 (man-computer+4)%3-1 1 -1 0 1 -1

剪刀石头布相生相克,形成一个环,凡是具有环的特性的数学模型都可以考虑用取模运算,首先确定了man-computer和%3,然后再调整其它常数得到normalized的结果。

编码风格

gdb

看下面的程序:

#include <stdio.h>
int main(void)
{
    int i;
    char str[6] = "hello";
    char reverse_str[6] = "";
    printf("%s\n", str);
    for (i = 0; i < 5; i++)
    reverse_str[5-i] = str[i];
    printf("%s\n", reverse_str);
    return 0;
}

首先用字符串"hello"初始化一个字符数组str(算上'\0'共6个字符)。然后用空字符串""初始化一个同样长的字符数组reverse_str,相当于所有元素用'\0'初始化。然后打印str,把str倒序存入reverse_str,再打印reverse_str。然而结果并不正确: $ ./main hello 我们本来希望reverse_str打出来是olleh的,结果什么都没有。重点怀疑对象肯定是循环,那么简单验算一下,i=0时,reverse_str[5] = str[0],也就是'h',i=1时,reverse_str[4] = str[1],也就是'e',依此类推,i=0,1,2,3,4,共5次循环,正好把h,e,l,l,o五个字母给倒过来了,哪里不对了? 请调试修正这个Bug。

程序把'\0'也换到开头来了。应该改成 reverse_str[4-i] = str[i];

排序与查找

实现选择排序算法

code

1、为了便于初学者理解,本节的merge函数写得有些啰嗦,你能想出哪些办法将它化简?

code

2、快速排序是另外一种采用分而治之策略的排序算法,在平均情况下的时间复杂度也是Θ(nlgn),但比归并排序有更小的时间常数。它的基本思想是这样的: int partition(int start, int end) { 从a[start..end]中选取一个pivot元素(比如选a[start]为pivot); 在一个循环中移动a[start..end]的数据,将a[start..end]分成两半, 使a[start..mid-1]比pivot元素小,a[mid+1..end]比pivot元素大,而a[mid]就是pivot元素; return mid; } void quicksort(int start, int end) { int mid; if (end > start) { mid = partition(start, end); quicksort(start, mid-1); quicksort(mid+1, end); } } 请补完partition函数,这个函数有多种写法,请选择时间常数尽可能小的实现方法。想想快速排序在最好和最坏情况下的时间复杂度是多少?快速排序在平均情况下的时间复杂度分析起来比较复杂,有兴趣的读者可以参考[算法导论]。

    int partition(int a[], int start, int end)
    {
        int pivot = a[end];
        while(start < end) {
            while(start<end && a[start]<=pivot)
                ++start;
            a[end] = a[start];
            while(start<end && a[end]>=pivot)
                --end;
            a[start] = a[end];
        }
        a[start] = pivot;
        return start;
    }

1、实现一个算法,在一组随机排列的数中找出最小的一个。你能想到的最直观的算法一定是Θ(n)的,想想有没有比Θ(n)更快的算法?

没有更快的算法,理由同上。

2、在一组随机排列的数中找出第二小的,这个问题比上一个稍复杂,你能不能想出Θ(n)的算法?

secondmin.c

3、进一步泛化,在一组随机排列的数中找出第k小的,这个元素称为k-th Order Statistic。能想到的最直观的算法肯定是先把这些数排序然后取第k个,时间复杂度和排序算法相同,可以是Θ(nlgn)。这个问题虽然比前两个问题复杂,但它也有平均情况下时间复杂度是Θ(n)的算法,将上一节习题1的快速排序算法稍加修改就可以解决这个问题: / 从start到end之间找出第k小的元素 / int order_statistic(int start, int end, int k) { 用partition函数把序列分成两半,中间的pivot元素是序列中的第i个; if (k == i) 返回找到的元素; else if (k > i) 从后半部分找出第k-i小的元素并返回; else 从前半部分找出第k小的元素并返回; } 请编程实现这个算法。

    int order_statistic(int a[], int start, int end, int k)
    {
        int i;
        if(end >= start) {
            i = order_partition(a, start, end, k);
            if(k == i)
                return a[i];
            else if(k > i)
                return order_statistic(a, i+1, end, k);
            else
                return order_statistic(a, start, i-1, k);
        }
    }

ordstat.c。可以分析一下为什么时间复杂度是Θ(n),在最好情况下每次丢掉一半的元素,n+n/2+n/4+n/8+...=2n,平均情况下的分析比较复杂,但快速排序类似,时间复杂度和最好情况下一致。

1、本节的折半查找算法有一个特点:如果待查找的元素在数组中有多个则返回其中任意一个,以本节定义的数组int a[8] = { 1, 2, 2, 2, 5, 6, 8, 9 };为例,如果调用binarysearch(2)则返回3,即a[3],而有些场合下要求这样的查找返回a[1],也就是说,如果待查找的元素在数组中有多个则返回第一个。 请修改折半查找算法实现这一特性。

    int binary_search(int a[], int low, int high, int element)
    {
        int mid = -2;
        while(low <= high) {
            mid = (low+high)/2;
            if(a[mid] == element) {
                while(a[mid-1] == element)/* return first element */
                    --mid;
                break;
            }
            else if(element < a[mid])
                high = mid-1;
            else
                low = mid+1;
        }
        if(low > high)
            return -2;
        return mid;
    }

2、编写一个函数double mysqrt(double y);求y的正平方根,参数y是正实数。我们用折半查找来找这个平方根,在从0到y之间必定有一个取值是y的平方根,如果我们查找的数x比y的平方根小,则x2y,我们可以据此缩小查找范围,当我们查找的数足够准确时(比如满足|x2-y|<0.001),就可以认为找到了y的平方根。思考一下这个算法需要迭代多少次?迭代次数的多少由什么因素决定?

    double mysqrt(double y)
    {
        double low=0, high=y, mid = -2;
        while(low < high) {
            mid = (low+high)/2;
            if(abs(pow(mid, 2)-y) < 0.00001)
                break;
            else if(pow(mid, 2) < y)
                low = mid+0.00001;
            else
                high = mid-0.00001;
        }
        return mid;
    }

迭代次数取决于精度(可以用对数函数表示)

3、编写一个函数double mypow(double x, int n);求x的n次方,参数n是正整数。最简单的算法是: double product = 1; for (i = 0; i < n; i++) product *= x; 这个算法的时间复杂度是Θ(n)。其实有更好的办法,比如mypow(x, 8),第一次循环算出x·x=x2,第二次循环算出x2·x2=x4,第三次循环算出4·x4=x8。这样只需要三次循环,时间复杂度是Θ(lgn)。思考一下如果n不是2的整数次幂应该怎么处理。请分别用递归和循环实现这个算法。

从以上几题可以看出,折半查找的思想有非常广泛的应用,不仅限于从一组排好序的元素中找出某个元素的位置,还可以解决很多类似的问题。[编程珠玑]对于折半查找的各种应用和优化技巧有非常详细的介绍。

栈和队列

1、修改本节的程序,要求从起点到终点正向打印路线。你能想到几种办法?

dfs.c

2、本节程序中predecessor这个数据结构占用的存储空间太多了,改变它的存储方式可以节省空间,想想该怎么改。

计算出row*MAX_ROW+col,把结果保存到predecessor数组,可节省一半的存储,需要座标时可以用/MAX_ROW和%MAX_ROW运算把row和col分离出来

3、上一节我们实现了一个基于堆栈的程序,然后改写成递归程序,用函数调用的栈帧替代自己实现的堆栈。本节的DFS算法也是基于堆栈的,请把它改写成递归程序,这样改写可以避免使用predecessor数据结构,想想该怎么做。

new

1、本节的例子直接在队列元素中加一个指针成员表示前趋,想一想为什么上一节的例 12.3 “用深度优先搜索解迷宫问题”不能采用这种方法表示前趋?

因为出栈的元素被新入栈的元素覆盖了

2、在讲解例 12.1 “用堆栈实现倒序打印”时我们说“top总是指向栈顶元素的下一个元素”是堆栈 操作的Class Invariant,那么本节实现的队列操作的Invariant应该怎么描述?

xy

2、本节例子中给队列分配的存储空间是512个元素,其实没必要这么多,那么解决这个问题至少要分配多少个元素的队列空间呢?跟什么因素有关?

每个点最多入队一次,迷宫中有多少个点就最多需要多大的队列空间

1、现在把迷宫问题的要求改一下,只要求程序给出最后结论就可以了,回答“有路能到达终点”或者“没有路能到达终点”,而不需要把路径打印出来。请把例 12.4 “用广度优先搜索解迷宫问题”改用环形队列实现,然后试验一下解决这个问题至少需要分配多少个元素的队列空间。

bfs_circular.c

注意用%运算移动head和tail,具有环的特性的数学模型都可以考虑用取模运算。队列空的判断条件是head == tail;,队列满的判断条件是head == (tail + 1) % QSIZE;,必须保留一个元素未用,否则无法区分队空和队满。

1、将例 12.4 “用广度优先搜索解迷宫问题”改用环形队列实现。然后回答: 运行原来的程序要求queue数组至少有多长?不用跟踪程序的运行过程,你能很快答上来 吗? 改为环形队列之后要求queue数组至少有多长?

code

results matching ""

    No results matching ""