函数及递归《算法入门经典》第四章(由于能力和时间有限没有做习题)

一)自定义函数与结构体:

1.在算法竞赛中,请总是让main函数返回0,以免评测系统错误地认为程序异常退出了。

2.typedef struct { 域定义; }类型名,就可以像原生数据类型一样使用这个自定义类型

3.即使最终答案在所选择的数据类型范围之内,计算的中间结果仍然可能溢出。

比如猴子吃桃问题;

对复杂的表达式进行化简有时不仅能减少计算量,还能减少甚至避免中间结果溢出。

a.计算组合数 n!/(m!(n-m)!) ,先进行约分:n!/m!=(m+1)(m+2)…(n-1)n。

代码:

long long C(int n, int m) {
if(m < n-m) m = n-m; //小技巧:当m<n-m时把m变成n-m
long long ans = 1;
for(int i = m+1; i <= n; i++) ans *= i;
for(int i = 1; i <= n-m; i++) ans /= i;
return ans;
}


b.素数判定:只判断不超过sqrt(x)的整数i
一旦发现x有一个大于1的因子,立刻返回0(假),只有最后才返回1(真)。

int is_prime(int n)
{
if(n <= 1) return 0;
int m = floor(sqrt(n) + 0.5); //floor()函数向下取整,加上5后,变成4舍5入
for(int i = 2; i <= m; i++)
if(n % i == 0) return 0;
return 1;
}
二)函数调用与参数传递

函数的形参和在函数内声明的变量都是该函数的局部变量。 无法访问其他函数的局部变量。 局部变量的存储空间是临时分的,函数执行完毕时,局部变量的空间将被释放,其中的值无法保留到下次使用。 在函数外声明的变量是全局变量,可以被任何函数使用。 操作全局变量有风险,应谨慎使用

调用栈: 它由多个栈帧(Stack Frame)组成,每个栈帧对应着一个未运行完的函数。 栈帧中保存了该函数的返回地址和局部变量,因而不仅能在执行完毕后找到正确的返回地址,还很自然地保证了不同函数间的局部变量互不相干

1.用函数交换量代码:

void swap(int* a, int* b)
{
int t = *a; *a = *b; *b = t;
}

C语言的变量都是放在内存中的,而内存中的每个字节都有一个称为地址(address)的编号。 每个变量都占有一定数目的字节(可用sizeof运算符获得),其中第一
个字节的地址称为变量的地址。

*a = *a + 1就是让a指向的变量自增1。 甚至可以把它写成(*a)++。 注意不要写成*a++,因为“++”运算符的优先级高于“取内容”运算符“*”,

void swap(int* a, int* b)//不正确
{
int *t;
*t = *a; *a = *b; *b = *t;
}
分析:因为t是一个变量(指针也是一个变量,只不过类型是“指针”),所以根据规则,它在赋值之前是不确定的。 如果这个“不确定的值”所代表的内存单元恰好是能写入的,那么这段程序将正常工作;但如果它是只读的,程序可能会崩溃。

2.计算数组元素和

int sum(int a[]) {//不正确
int ans = 0;
for(int i = 0; i < sizeof(a); i++)ans += a[i];
return ans;
}

分析:因为sizeof(a)无法得到数组的大小。因为把数组作为参数传递给函数时,实际上只有数组的首地址作为指针传递给了函数。 换句话说,在函数定义中的int a[]等价于int*a。 在只有地址信息的情况下,是无法知道数组里有多少个元素的。

int sum(int* a, int n) {//直接把参数a写成了int* a,暗示a实际上是一个地址
int ans = 0;
for(int i = 0; i < n; i++)
ans += a[i];
return ans;
}

如果p1和p2是类型相同的指针,则p2-p1是从p1到p2的元素

3.快速排序算法(C语言的stdlib.h中有一个叫qsort的库函数)

void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void *) );
前3个参数分别是待排序的数组起始地址、 元素个数和每个元素的大小。 最后一个参数比较特别,是一个指向函数的指针,该函数应当具有这样的形式:

int cmp(const void *, const void *) { … }//指向常数的“万能”的指针:const void *,它可以通过强制类型转化变成任意类型的指针。

eg:

int cmp ( const void *a , const void *b ) {
return *(int *)a - *(int *)b;
}
一般地,需要先把参数a和b转化为真实的类型,然后让 cmp函数当a<b、 a=b和a>b时分别返回负数、 0和正数即可。

三)递归

在可执行文件中,正文段(Text Segment)用于储存指令,数据段(DataSegment)用于储存已初始化的全局变量,BSS段(BSS Segment)用于储存未赋值的全局变量所需的空间。调用栈在运行时创建。每次递归调用都需要往调用栈里增加一个栈帧,久而久之就越界了。 这种情况叫做栈溢出(Stack Overflow)

建议:把较大的数组放在main函数外,局部变量也是放在堆栈段的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值