本笔记记录自 Coursera课程 《计算机程式设计》 台湾大学 刘邦锋老师

Week3 Array

3-1 Array Usage

例子:使用数组一次申明10个整数变量

int a[10]

这样就一次申明了10个整数的变量,a后面的方括号[10]表示a是一个有10个元素的整数数组。

所以说C语言中数组是用[ ]表示的。

【注】

  • 一个数组和一个变量一样,有类别、名字、值、位址等属性,但数组还多了一个属性,就是数组中有几个元素。
  • 因为数组中有多个元素,我们必须用一个数字代表我们要使用的是哪一个。这个数字就称为标注(index)。
  • 与一般的数学向量惯例不同,C语言的数组标注是由0开始的。a[0]是数组a的第一个元素。

例子:(print-array.c)印出数组中元素的值

#include <stdio.h>

main()
{
int a[10];
int i;
for (i = 0; i < 10; i++)
scanf("%d", &(a[i]));
for (i = 0; i < 10; i++)
printf("%d\n", a[i]);
}

3-2 Inner Product

例子:(inner-product.c)计算内积

#include <stdio.h>
main()
{
int A[5], B[5], C = 0;
int i, j;
for (i = 0; i < 5; i++)
scanf("%d", &(A[i]));
for (i = 0; i < 5; i++)
scanf("%d", &(B[i]));
for (i = 0; i < 5; i++)
C += A[i] * B[i];
printf("%d\n",C);
}

因为可以把数组看作是一个向量,两个向量之间就可以进行内积运算。

【注】这个程序的输入栏并不一定要一个数字一行,而是一个长度为5的向量一行,数字之间用一个空格隔开。这样会使输入更加清晰。因为scanf会在输入栏中持续找数字,一行没有找下一行,直到找到为止。所以对scanf完全没有影响。

3-3 Fibanacci Numbers

以费伯纳西数列为例,公式如下

\[fib(i)= \begin{cases} 0 & \text {i=0} \\ 1 & \text{i=1} \\ fib(i-1)+fib(i-2) & \text{i>=2} \end{cases}
\]

例子:(fib-array.c)计算费伯纳西数列到第n项

#include <stdio.h>
main()
{
int i;
int fab[100];
int n;
scanf("%d", &n);
fab[0] = 0;
fab[1] = 1;
for (i = 2; i < n; i++)
fab[i] = fab[i - 1] + fab[i - 2];
for (i = 0; i < n; i++)
printf("%d\n", fab[i]);
}

3-4 Prime Numbers

在数组中找到我们想要的数,比如说寻找第一个不为1的元素。

int array[10];
...
i = 0;
while (i < 10 && array[i] == 1)
i++;

例子:(prime-array.c)印出n之内的质数

#include <stdio.h>
main()
{
int composite[101];
int i, n, j = 2;
scanf("%d", &n);
for (i = 2; i <= n; i++)
composite[i] = 0;
while (j * j <= n){
while (composite[j] == 1)
j++;
for (i = 2 * j; i <= n; i += j)
composite[i] = 1;
j++;
}
for (i = 2; i <= n; i++)
if (composite[i] == 0)
printf("%d\n", i);
}

对于这个程序的思路是

  • 利用一个数组composite作是否为合数的旗标(flag)。如果j是一个合数,那么对应的composite[j]为1,否则j是一个质数,composite[j]为0。
  • 假设所有由2到n的整数都是质数。
  • 由2开始,找第一个还没被设为合数的整数j,并认定为质数。
  • 设定j的倍数为合数。
  • 将j加1,测试下一个数。
  • 只需测试到j * j <= n即可。

3-5 Bubble Sort

泡沫排序法

  • 由左到右比较两个相邻元素,如果标注比较小的元素比较大,则交换元素值。
  • 由标注比较小的元素两两交换到标注比较大的元素,就能使大的元素向标注比较大的方向移动,而小的元素向标注标比较小的方向移动。
  • 用两层for循环实现。
    • 第一层循环决定两两交换的范围。
    • 第二层则实现两两交换。

例子:(bubble-sort.c)泡沫排序法

#include <stdio.h>
int main()
{
int m, n[100];
int i, j, temp;
scanf("%d", &m);
for (i = 0; i < m; i++)
scanf("%d", &(n[i]));
for (i = m -2; i >= 0; i--)
for (j = 0; j <= i; j++)
if (n[j] > n[j + 1]){
temp = n[j];
n[j] = n[j + 1];
n[j + 1] = temp;
}
for (i = 0; i < m; i++)
printf("%d\n", n[i]);
return 0;
}

3-6 Array Address and Initialization

以十六进制打印出变量所在的记忆体位址

printf("%p\n", &i);

例子:(print-array-address.c)印出数组a中的元素的大小及位址

#include <stdio.h>
main()
{
int a[10];
int i; printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a));
for (i = 0; i < 10; i++)
printf("%p\n", &(a[i]));
printf("%p\n", &a);
printf("%p\n", a);
}

对于上面这个程序

  • a[0]是一个32位元的整数,所以会打印出4
  • a是10个32位元的整数所组成的数组,所以会打印出40
  • 数组a元素的记忆体位址是连续的,而且一个元素和下一个元素的位址刚好差4.
  • 数组元素在记忆体中是由小排到大,而且每个元素占4个位元组。

所以元素a[i]的记忆体位址可用以下公式表示

\[a+(i \times L)
\]

其中,a为阵列a的起始位址,而L为每一元素所占的位元组数。a的位址和a[0]的位址是一样的(也就是一排房子的位置,从第一栋房子开始算)。

在C语言中,a的值并非代表数组中所有元素的值,而代表的是数组a的位址

阵列的初始化

int array[5] = {1, 2, 3, 4, 5};

跟变量的初始化差不多。而且[ ]中的5也可以不写,程序会自己计算{ }中有多少个元素。

如果数组申明有长度,也有初始化,但是初始化给的元素个数不够,那么其他的元素会默认初始化为0。

3-7 Multi-dimension Arrays

多维数组

比如说申明一个$ 3 \times 4 $的多维数组。

int a[3][4];

例子:(matrix-multiply.c)矩阵相乘

#include <stdio.h>
main()
{
int A[2][3], B[3][4], C[2][4];
int i, j, k; for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
scanf("%d", &(A[i][j]));
for (i = 0; i < 3; i++)
for (j = 0; j < 4; j++)
scanf("%d", &(B[i][j]));
for (i = 0; i < 2; i++)
for (j = 0; j < 4; j++)
C[i][j] = 0;
for (i = 0; i < 2; i++)
for (j = 0; j < 4; j++)
for (k = 0; k < 3; k++)
C[i][j] += A[i][k] * B[k][j]; for (i = 0; i < 2; i++){
for (j = 0; j < 4; j++)
printf("%4d", C[i][j]);
printf("\n");
}
}

3-8 Multi-dimension Array Output with Newline

跟3-7差不多,主要将换行问题,可参考3-7最后C数组的输出方式。

3-9 Multi-dimension Array Address

例子:(print-matrix-address.c)三维数组大小及位址

#include <stdio.h>
main()
{
int a[2][3][4];
int i, j, k; printf("%d\n", sizeof(a[0][0][0]));
printf("%d\n", sizeof(a[0][0]));
printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a)); for (i = 0; i < 2; i++){
for (j = 0; j < 3; j++){
for (k = 0; k < 4; k++)
printf("%p ", &(a[i][j][k]));
printf("\n");
}
printf("\n");
} for (i = 0; i < 2; i++)
printf("%p ", &(a[i][1]));
printf("\n"); for (i = 0; i < 2; i++)
printf("%p\n ", &(a[i][1]));
printf("\n"); for (i = 0; i < 2; i++)
printf("%p\n", &(a[i]));
printf("\n"); for (i = 0; i < 2; i++)
printf("%p\n", a[i]);
printf("\n"); printf("%p\n", &a);
printf("%p\n", a);
}

【注】

  • $ a[0][0][0] $是一个4个位元组的整数
  • $ a[0][0] $是一个4个4个位元组的整数所组成的数组
  • a包含两个矩阵,每个矩阵有三列,每一列都是一个有四个元素的一位数组

多维数组元素$ a[i][j][k] $ 的记忆体位址可以用以下的公式计算

\[a+(i \times 3 \times 4 + j \times 4 + k) \times 4
\]

具体一点则\(a[k_1][k_2]...[k_n]\)的记忆体位址是

\[a+(\sum_{i=1}^{n-1} (k_i \times \prod_{j=i+1}^n m_j) + k_n ) \times L
\]

3-10 Multi-dimension Array Address Example

3-9的程序运行结果讲解。

3-11 Multi-dimension Array Initialization

二维数列的初始化

int array[2][3] = {{1, 2, 3}, {4, 5, 6}};

跟一维数组很相似。可以写成\(array[][3]\)但不能写成\(array[][]\)。因为后者编译器无法决定位址。

补0原则仍然适用于多维数组。

3-12 Floating Point Input Output

float及double变量的申明方法

float f;
double df;

浮点数可以表示小数点。C语言里有两种浮点数,就是float和double。

float是一般浮点数,通常占4个位元组。

double是倍准(double precision)浮点数,通常占8个位元组,具有较高的准确度。

浮点数float的输出及输入

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

倍准浮点数double的输出及输入

printf("%f\n", df);
scanf("%lf", &df);

输出时,浮点数float及倍准浮点数double百分号%后面一律加f,因为printf会将float升级到倍准浮点数double再印出。

输入时浮点数在百分号%后面加f,倍准浮点数加lf。

3-13 Type Casting

混合类别计算

当一个算式同时出现不同类别的变量时,C语言采用一种升级的概念,就是等级低的会先被升级成等级高的,然后再计算。

倍准浮点数double的等级最高,再来是浮点数float,最后才是整数int。

除了因为算式中出现不同类别而发生的潜在类别转换之外,有时我们也需要直接将算式的类别做转换,此时我们就需要使用转型(cast)

只要在算式前加一个用括号包住的类别,就可以将算式转换为该类别。

(type) expression

例子:(average.c)计算平均分数

#include <stdio.h>
int main()
{
int count = 0;
int sum = 0;
int grade;
double average; scanf("%d", &grade);
while (grade >= 0){
sum += grade;
count++;
scanf("%d", &grade);
}
average = sum / count;
printf("%f\n", average);
average = (double) sum / count;
printf("%f\n", average);
average = (double) (sum / count);
printf("%f\n", average);
}

3-14 Floating Point Computation

以计算\(e^x\) 的泰勒展开式为例。

\[e^x=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+...+\frac{x^n}{n!}
\]

思路是使用一个for循环来计算每一次的\(\frac{x^i}{i!}\)值。分子的部分存在x_power,分母的阶乘部分存在factorial。

例子:(e-x-float.c)以float计算\(e^x\)

#include <stdio.h>
mian()
{
float x;
float e = 1.0;
int i;
int n = 10;
int factorial = 1;
float xpower = 1.0; scanf("%f", &x);
for (i = 1; i <= n; i++){
factorial *= i;
xpower *= x;
e += xpower / factorial;
}
printf("%f\n", e);
}

上面的程序中变量factorial是来计算分母的阶乘的。而阶乘增加的非常快,当输入的n稍大时,变量factorial 就会发生溢位,影响计算结果。

改善的方法是不要直接计算分子分母,而是使用一个变量term记住目前的第i项\(\frac{x^i}{i!}\),然后调整成第i+1项\(\frac{x^{i+1}}{(i+1)!}\)即可。

#include <stdio.h>
mian()
{
double x;
double e = 1.0;
int i;
int n = 20;
double term = 1.0; scanf("%lf", &x);
for (i = 1; i <= n; i++){
term *= (x/i);
e += term;
}
printf("%f\n", e);
}

测验代码

啊……这次测验要写的代码好难啊…………

贴出老师的代码

#include <stdio.h>

int main ()
{
int n, m;
int num;
int w_flag = 0;
scanf ( "%d%d", &n, &m ); int board[n][m][m];
int bingo[n][2][m+1]; for ( int p = 0; p < n; p++ ) {
for ( int i = 0; i < m; i++ ) {
bingo[p][0][i] = bingo[p][1][i] = 0;
for ( int j = 0; j < m; j++ )
scanf ( "%d", &board[p][i][j] );
}
bingo[p][0][m] = bingo[p][1][m] = 0;
} while ( !w_flag ) {
// read number
scanf ( "%d", &num ); // for each player find number in their board
for ( int p = 0; p < n; p++ ) {
int f_flag = 0;
for ( int i = 0; !f_flag && i < m; i++ )
for ( int j = 0; !f_flag && j < m; j++ )
// mark if find
if ( board[p][i][j] == num ) {
f_flag = 1;
// row and column
bingo[p][0][i]++;
bingo[p][1][j]++;
// diagnal
if ( i == j ) bingo[p][0][m]++;
if ( i + j == m-1 ) bingo[p][1][m]++;
// win
if ( bingo[p][0][i] == m || bingo[p][1][j] == m ||
bingo[p][0][m] == m || bingo[p][1][m] == m ) {
if ( !w_flag ) {
printf ( "%d", num );
w_flag = 1;
}
printf ( " %d", p );
}
}
} } return 0;
}

老师是建议大家可以在ideone上进行代码的编译的,但是这个在线的网站是要魔法上网的,不过毕竟大家都coursera上看到了视频应该都会魔法上网了。

但是有一个小建议,可以现在notepad++这类软件上先把代码写好再复制粘贴到网页上编译,而不是直接在网页上写。因为……很容易手贱一刷新网页就崩了!写了的代码全没了的情况,别问我是怎么知道的。

《计算机程式设计》Week3 课堂笔记的更多相关文章

  1. 【C语言】Coursera课程《计算机程式设计》台湾大学刘邦锋——Week6 String课堂笔记

    Coursera课程 <计算机程式设计>台湾大学 刘邦锋 Week6 String 6-1 Character and ASCII 字符变量的声明 char c; C语言使用一个位元组来储 ...

  2. 《计算机程式设计》Week5 课堂笔记

    本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week5 Pointer 5-1 Pointer Definition and Declaration 指针 ...

  3. 《计算机程式设计》Week4 课堂笔记

    本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week4 Functions 4-1 System Function 函数主要分为两大类系统定义函数与使用者 ...

  4. 《计算机程式设计》Week2 课堂笔记

    本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week2 Control Structure 2-1 If-then-else if then 判断 if ...

  5. 九章算法系列(#3 Binary Tree & Divide Conquer)-课堂笔记

    前言 第一天的算法都还没有缓过来,直接就进入了第二天的算法学习.前一天一直在整理Binary Search的笔记,也没有提前预习一下,好在Binary Tree算是自己最熟的地方了吧(LeetCode ...

  6. 九章算法系列(#5 Linked List)-课堂笔记

    前言 又是很长时间才回来发一篇博客,前一个月确实因为杂七杂八的事情影响了很多,现在还是到了大火燃眉毛的时候了,也应该开始继续整理一下算法的思路了.Linked List大家应该是特别熟悉不过的了,因为 ...

  7. 九章算法系列(#4 Dynamic Programming)-课堂笔记

    前言 时隔这么久才发了这篇早在三周前就应该发出来的课堂笔记,由于懒癌犯了,加上各种原因,实在是应该反思.好多课堂上老师说的重要的东西可能细节上有一些急记不住了,但是幸好做了一些笔记,还能够让自己回想起 ...

  8. 九章算法系列(#2 Binary Search)-课堂笔记

    前言 先说一些题外的东西吧.受到春跃大神的影响和启发,推荐了这个算法公开课给我,晚上睡觉前点开一看发现课还有两天要开始,本着要好好系统地学习一下算法,于是就爬起来拉上两个小伙伴组团报名了.今天听了第一 ...

  9. ocp11g培训内部教材_052课堂笔记(042)_体系架构

    OCP 052 课堂笔记 目录 第一部分: Oracle体系架构... 4 第一章:实例与数据库... 4 1.Oracle 网络架构及应用环境... 4 2.Oracle 体系结构... 4 3. ...

随机推荐

  1. Chrome开发者工具详解(二)之使用断点调试代码下

    JS调试技巧 技巧一:格式化压缩代码 技巧二:快速跳转到某个断点的位置 右侧的Breakpoints会汇总你在JS文件所有打过的断点,点击跟checkbox同一行的会暂时取消这个断点,若是点击chec ...

  2. spring.jar是包含有完整发布的单个jar 包,spring.jar中包含除了spring-mock.jar里所包含的内容外其它所有jar包的内容,因为只有在开发环境下才会用到 spring-mock.jar来进行辅助测试,正式应用系统中是用不得这些类的。

    Spring jar包的描述:针对3.2.2以上版本 org.springframework spring-aop ——Spring的面向切面编程,提供AOP(面向切面编程)实现 org.spring ...

  3. python之路之——操作系统的发展历史

    阅读目录 手工操作 —— 穿孔卡片 批处理 —— 磁带存储和批处理系统 多道程序系统 分时系统 实时系统 通用操作系统 操作系统的进一步发展 操作系统的作用 手工操作 —— 穿孔卡片 1946年第一台 ...

  4. 利用 TCMalloc 优化 Nginx 的性能

    TCMalloc 全称为 Thread-Caching Malloc,是谷歌的开源工具 google-perftools 的成员,它可以 在内存分配效率和速度上高很多,可以很大程度提高服务器在高并发情 ...

  5. 前端div+css

    css基本结构: css的四种引入方式: 1.行内式:是在标记的style属性中设定CSS样式.这种方式没有体现出CSS的优势,不推荐使用. <p style="background- ...

  6. c/c++基础篇之数据类型转换

    C/C++常见的数据类型转换 1.      常见的单类基本类型转换 (1)强制类型转换 如: int a=(int)(9.87)  结果a=9 char c=(char)(97)  结果为c=’a’ ...

  7. 一、left

    一.left - right 就是遍历(以左边遍历,以右边遍历) inner join 就是求公共部分的结果集 left join 查询结果 right join结果 inner join 解决的办法 ...

  8. fs.mkdir

    fs.mkdir(path[, mode], callback) 要求父目录必须存在 let fs = require('fs'); fs.mkdir('./c/b/a', res=>{ // ...

  9. RPC vs REST

    RPC vs REST 另外,由于Dubbo是基础框架,其实现的内容对于我们实施微服务架构是否合理,也需要我们根据自身需求去考虑是否要修改,比如Dubbo的服务调用是通过RPC实现的,但是如果仔细拜读 ...

  10. CentOS6.5 安装gitlab以及gitolite迁移gitlab

    CentOS6.5 安装gitlab以及gitolite迁移gitlab gitlab 的安装使用以及数据结构 安装 环境: CentOS6.5 基于 nignx + unicorn 搭建的应用环境, ...