物联网技术部&秘书部第一次软件培训总结
文章目录
一、二维数组
1.定义
类型说明符 数组名 [常量表达式1] [常量表达式2]
float a[3][4];
数组中的元素为:
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
2.初始化
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int a[3][4]={{1},{0,6},{0,0,11}};
int a[3][4]={1,2,3};
int a[ ][3]={{1},{2},{3,4,5}}; //即 a[3][3]
int a[ ][3]={1,2,3,4,5}; //即 a[2][3]
注意: 下标不能越界!
3.引用
数组名[常量表达式1][常量表达式2]
//示例:
b[1][2] = a[2][3]/2
4.应用
请设计一个程序,将一个4行4列的二维数组中行和列元素互换,并存放到另一个数组中。
小代码示例
//C语言
#include<stdio.h>
int main(){
int a[4][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14};
int t;
for (int i=0; i < 4; i++) {
for (int j = 0; j < 4; j++)
printf("%3d", a[i][j]);
printf("\n");
}
for (int i = 0; i < 4; i++)
for (int j = i + 1; j < 4; j++) {
t = a[i][j];
a[i][j]=a[j][i] ;
a[j][i] = t;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
printf("%3d", a[i][j]);
printf("\n");
}
return 0;
}
// C++
#include<iostream>
#include<iomanip>
using namespace std;
int main(){
int a[4][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14};
int t;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
cout << setw(3) << setfill(' ') << a[i][j];
cout << endl;
}
for (int i = 0; i < 4; i++)
for (int j = i + 1; j < 4; j++) {
t = a[i][j];
a[i][j]=a[j][i] ;
a[j][i] = t;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
cout << setw(3) << setfill(' ') << a[i][j];
cout << endl;
}
return 0;
}
二、指针
1.概念
变量的值: 变量所代表的内存单元中的内容
变量的地址: 该变量所占存储单元的首地址
指针: 一个变量的地址,一个内存单元的地址,通常是一个无符号整数
指针变量: 存放变量地址的变量
2.定义与使用
类型说明符 *变量名
int *i_pointer , i=10;
i_pointer=&i;
printf(“%d”,*i_pointer);
说明: * 解引用 & 取地址符
小代码示例:
int main(){
int i = 10;
int *p;
*p = i; //危险!
printf("%d", *p);
return 0;
}
注意: 指针变量必须先赋值,再使用!
3.指针与一维数组
1)一维数组的地址
2)关系
int a[10], *pa;
pa=a; pa =&a[0]; //等价
引用一个数组元素,有3种方法:
1)下标法: a[ i ]
2)数组名地址法: * (a+i )
3)指针法:
①指针地址法: * (pa+i )
②指针下标法: pa[ i ]
4.指针与多维数组
1)多维数组的地址
int a[3][4]={0};
a代表二维数组首行的首地址,记作a[0]或&a[0][0](现在的首元素不是一个简单的整型元素,而是由4个整型元素所组成的一维数组)
同理a+1为序号为1的行的首地址,记作a[1]==&a[1][0]
那1行1列的元素的地址? a[1]+1
2)关系
指向数组元素的指针变量
int a[3][4]={0};
int *p;
p = a[0];
p++; //p可以向下移动,依次指向下一个元素
指向数组的行指针变量
int a[3][4]={0};
int (*p)[4];
p = a;
a[i][j] == *(*(p+i)+j)
5.多级指针
如果指针变量中存放的是另一个指针变量的地址,就称该指针变量为指向指针的指针变量,也称为二级指针。
数据类型 **变量名
int i=10,*ptrch=&i;
int **pp=&ptrch;
6.malloc动态分配
void *malloc(unsigned int size);
int a[n]; !错误
int *a=(int *)malloc(n*sizeof(int));
//或
#define N 100
int a[N]
7.指针小应用
用指针改写二维数组中的矩阵转置
int *p=a;
int (*q)[4]=a;
a[i][j] = p[i*n+j] = *(p+(i*n+j));
a[i][j] = q[i][j] = *(*(q+i)+j);
三、函数
1.定义与声明
定义
类型名 函数名(形参表)
{
声明部分
执行语句
}
声明
类型名 函数名(形参表);
2.递归函数
如果一个函数在其函数体中直接或者间接地调用了自己,则该函数称为递归函数。
直接调用自己为直接递归;间接调用自己为间接递归。
图解如下:
1)从数学角度看递归
例如
等差数列:
首项,即第一项的值a1;
递推公式,即连续项间的关系(an = an-1 +9)
当调用a(n)即执行a(n-1)+9,同时调用a(n-1)即a(n-2)+9……这样一直下去,最后到首项时,不再调用函数,直接返回数值。此时一路反向加回去,结束递归。
图解如下:
2)递归的注意点
1)结束条件:递归需要有明确的终止条件(首项)
2)逼近过程:处理好问题和子问题之间的关系,使得每次递归调用都要逼近终止条件
3)初始参数:调用递归函数的参数
3)递归的应用
1.请设计一个程序,利用递归求n!
小代码示例:
// C语言
#include<stdio.h>
int multiple(int n) {
if (n == 1)
return 1;
else
return n*multiple(n - 1);
}
int main() {
int n,m;
scanf("%d", &n);
m = multiple(n);
printf("%d", m);
return 0;
}
// C++
#include<iostream>
using namespace std;
int multiple(int n) {
if (n == 1) {
return 1;
}else {
return n*multiple(n - 1);
}
}
int main() {
int n,m;
cin >> n;
m = multiple(n);
cout << m;
return 0;
}
2.有一对兔子,从出生后第三个月起每个月都生一对兔子,小兔子长到第三个月后,每个月又生一对兔子。假设兔子都不死,问第20个月的兔子的对数为多少
小代码示例
// C语言
#include<stdio.h>
int birth(int n) {
int result;
if (n == 1)
result = 1;
else if (n == 2)
result = 1;
else if (n == 3)
result = 2;
else
result = birth(n-1)+birth(n-3);
return result;
}
int main()
{
int n,x;
scanf(“%d”,&n);
x=birth(n);
printf(“%d”,x);
return 0;
}
// C++
#include<iostream>
using namespace std;
int birth(int n) {
int result;
if (n == 1)
result = 1;
else if (n == 2)
result = 1;
else if (n == 3)
result = 2;
else
result = birth(n-1)+birth(n-3);
return result;
}
int main()
{
int n,x;
cin >> n;
x=birth(n);
cout << x;
return 0;
}
3.函数的优缺点
函数用于包装一些代码片段,使得他能够方便的被重复使用。
函数主要有以下的几个优点:
1)函数是实现代码重用的一种方式
2)友好的函数名字便于传达代码的含义
3)函数会简化代码的实现,使思路更清晰
同时,函数主要也有以下的几个缺点:
1)调用函数会时间和空间的消耗
2)递归函数拥有栈溢出的风险
四、OJ小技巧
1.关于输入问题
1)做题的时候尽量使用scanf printf。cin cout比scanf printf慢20倍左右,一旦遇到大数据量,光是读入就有可能跪掉。
2)scanf和cin混用可能就会造成一些奇怪的错误。
3)关于输入多组数据
#include <stdio.h>
int main(){
int input;
while(scanf("%d",&input) != EOF) {
//在此处理数据
printf("%d\n",input);
}
return 0;
}
还可以:
while( ( scanf(“%d”,&a) ) != -1 )
while( ( scanf(“%d”,&a) ) == 1 )
while( ~( scanf(“%d”,&a) ) )
while(cin>>a) //C++
读到一个0时,程序结束,可用:
while( scanf(“%d”,&a) ,a)
while( scanf(“%d”,&a) &&a!=0)
while (cin>>i,i) //C++
2.关于输出格式(presentation error)
1)行末空格:比如我输出需要打印多个数需要使用空格分隔的时候,我们循环使用printf(“%d “,x);这种会很方便,但是这样会导致行末多一个空格,后台系统会严格比对你的输出和.out文件,这样也会被判错误
2)换行问题:对于每个样例,建议输出完全之后都换行一下。对于一些题目,可能就是不换行就导致了后面输入数据错位,那就肯定不可能过了。
3)大部分题处理一组数据后可直接输出,不需要用数组保存每一个Case的数据。
3.关于数组问题
1)数组定义int a[10]={0};可以对其全部元素赋值为0;但是数组太大不要这样。
2)定义数组时,数组大小最好比告诉的最大范围大一点。
3)字符数组大小必须比字符串最大长度大1。
4)处理字符数组时不要忘了在最后加’/0’或者0。
4.关于Runtime error
1)下标越界
2)要表示的数值太大,超出了定义类型数的范围。
3)未赋值的变量就直接使用
4)在无限递归或函数里使用了太大的数组变量
!一定要好好排查,不仔细一般找不出来。
5.关于字符串
1)纯字符串用puts()输出,会增快速度。
2)先用scanf(),再用gets()会读入回车。要使用getchar()吸收空格和回车的录入。
!使用c语言读字符和字符串一定要十分小心。尽量写好就自己输出一下看看是否是自己需要的值被读入。
6.总结
1)输入输出是不是按照题意进行的。
2)是不是有些步骤没有完成。
3)对于边界输入的检查
4)算法的复杂度是不是过高
牢记:
代码不通过肯定是自己哪里出了什么问题,检查代码!再三看题读懂题意!!!
7.OJ小例题
输入
输入包含多个测试用例。每个测试用例以数字N开始(0 < N <= 1000)——分布的气球总数。接下来的N行每一行都包含一种颜色。气球的颜色是由15个小写字母组成的字符串。
输出
对于每一种情况,打印气球颜色最流行的问题在一行。保证每个测试用例都有唯一的解决方案。
sample input:
orange
orange
pink
red
blue
sample
output:
orange
代码小示例:
#include <stdio.h>
#include <string.h>
main(){
int n, i, j, t, max, num[1000];
char color[1000][16];
while(scanf("%d", &n) != EOF){
if(n){
num[0]=0;
scanf("%s", color[0]);
for(i=1; i <n; i++){
num[i]=0;
scanf("%s", color[i]);
for(j=0; j <i-1; j++)
if(strcmp(color[i], color[j])==0) num[i] +=1;
}
max=num[0];
t=0;
for(i=1; i <n; i++)
if(max <num[i]) {max =num[i]; t=i;}
printf("%s\n",color[t]);
}
}
}