目录
1.将十进制数num的每一位放进数组arr中
思路:将num%10的值放入数组arr中,然后num/10,依次不断存入个位、十位、百位。。。
(不过下面一串代码运行起来好像有点问题,容我下去改正改正 Date7.25)
void extract(int num, int* arr) { //将十进制数num的每一位放进数组arr中
int* ret = arr;
while(num>0){
*arr= num % 10;
arr++;
arr = (int*)malloc(sizeof(int));
num /= 10;
}
arr = ret;
}
int main() {
int n = 1234;
int arr[4] = {0};
extract(n, arr);
for (int i = 0; i < 4; i++) {
printf("%d\n", *(arr + i));
}
free(arr); // 释放内存
arr=NULL;
return 0;
}
Date8.2 检查错误
上述代码有一个问题,就是自定义函数extract中ret指针后面的空间是一块一块开辟的而不是一下子开辟若干个空间。这就使得每次执行arr = (int*)malloc(sizeof(int)),就是在堆区开辟一块空间,然后将这块空间的首地址赋值给arr。因为我们不知道arr+1的地址所对应的空间是否空闲,因此每次新开辟的空间不一定与前一次连续,这就导致了每次循环中arr的地址都是随机值。我们可以打印检验一下:
如图,可以很直观的看到前后两次arr的不同,我们不能一块一块地开辟空间,这样的空间不连续。
改进:我们可以事先求出num数字的位数,然后再开辟固定大小的空间。
int* extract(int num) { //将十进制数num的每一位放进数组arr中
int i = 0,n=num;
while (n>0) {
n /= 10;
i++;
}
int* arr = (int*)malloc(sizeof(int)*i);
int* ret = arr;
while (num > 0) {
*arr++ = num % 10;
num /= 10;
}
arr = NULL;
return ret;
}
或者,我们也可以使用realloc()函数来改变当前开辟空间的大小。
2.计算S=1/1!+1/2!+...+1/n!
思路:1.先自定义函数求n!2.强制类型转换为float型并求和输出
若需保留2位小数,则打印格式为"%.1f"
int factorial(int n) {//求n的阶乘
if (n == 1)
return 1;
return n*factorial(n - 1);
}
float factorial_s(int n) {//求和
float s=0,num;
for (int i = 1; i <= n; i++) {
num = (float)factorial(i);
s +=1/(num) ;
}
return s;
}
3.输出n个数中三个数的排列
例如:
arr=[1,2,3,4,5];
输出1 2 3,1 3 2,2 1 3,2 3 1......1 2 4,1 4 2......1 2 5......2 3 4......
思路:写两个函数,分开使用,两个函数分别有以下功能
1.从n个数中选出三个数
2.对这3不同排列输出
(注意:在取数的时候为了避免重复取,比如第一次取了1 2 3,后面又有一次取了2 1 3。我们可以设置成第二次取的数下标必须大于第一次的,第三次取的数下标必须大于第二次的)
3.1 非递归(较容易想到)
思路:1.从n个数中选出三个数
2.不同排列输出
void print(int i, int j, int k) {//输出三个数的序列
printf("%d %d %d;", i, j, k);
printf("%d %d %d;", i, k, j);
printf("%d %d %d;", j, i, k);
printf("%d %d %d;", j, k, i);
printf("%d %d %d;", k, i, j);
printf("%d %d %d\n", k, j, i);
}
void chooseprint(int arr[], int n) {//在n个数中找三个数并输出
for (int i = 0; i < n - 2; i++)//第一个数从0号下标开始
for (int j = i + 1; j < n - 1; j++)//第二个数下标要大于第一个数,从i+1开始
for (int k = j + 1; k < n; k++)//第三个数下标要大于第二个数,从j+1开始
print(arr[i], arr[j], arr[k]);
}
这种缺点很明显,三层for循环,时间复杂度较高;若输出r个数的序列,则需要r层循环,且输出方法较为死板
3.2 递归(较难)
3.2.1 从n个数中选出r个数
在上例({1,2,3,4,5}中取三个数)中,当第一个数选的是1时,我们可以画出下图。下图的过程实则表示一个递归树,一开始相当于我们在5个数里面取3个数,然后每次取出一个数之后按照同样的规则取下一个数:(一.5个里面取3个;二.4个里面取2个;三.3个里面取1个)
取出一个数后,为了方便跳转和更改,我们新建一个元素个数为r的数组data[]来存放取出的数。
代码如下:
//一开始index和i均为0
void combination(int arr[], int n, int r, int index, int data[], int i) {//arr是原数组,大小为n;index表示已取出的个数;data[]存放取出的数据,大小为r;i为arr当前遍历的元素下标
if (index == r) {//当已经取出r个数时
recursivePermutation(data, 0, r-1);//执行输出函数,输出这r个数的全排列
return;
}
if (i == n)//当arr数组遍历完最后一个元素后,停止往后遍历,退出递归
return;
data[index] = arr[i];//取出下标为i的数到data
combination(arr,n,r,index+1,data,i+1);//继续递归取出下一个数,直至取满
combination(arr, n, r, index, data, i + 1);//更改数组data中下标为index的数
}
可以类似递归入栈的过程,越后面放入的数越难在栈中拿出来和修改,因此递归的过程就是不断取数入栈,当栈内放满r个数后先不断更改栈顶的元素,每一次更改都是一次出栈、入栈操作;栈顶的元素更改完之后开始不断更改离栈顶最近的元素,直至更改完栈底元素。
3.2.2 r个数全输出
现在已经把取到的数放进了data数组中。当输出某个时刻,“先输出第2个数,再输出第1个数......”这样输出会比较难受。我们不妨交换先第1,2个数的位置,再顺序输出数组,最后输出完之后再换回来,使其保持一开始的顺序。用start和i来表示要交换的元素下标,代码如下:
// 交换数组元素位置
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 递归方式输出全排列,一开始start=0,end=r-1
void recursivePermutation(int* data, int start, int end) {
if (start == end) {//
for (int i = 0; i <= end; i++) {
printf("%d ", data[i]);
}
printf("\n");
}
else {
for (int i = start; i <= end; i++) {//遍历数组需要交换的位置
swap(&data[start], &data[i]);//交换位置
recursivePermutation(data start + 1, end);
swap(&data[start], &data[i]);//在换回来保持原样
}
}
}