C语言精选50题--思想讲解(1/5)

文章介绍了递归和非递归算法在求阶乘、字符串长度、字符串逆序、数字位和、幂运算和斐波那契数列等计算中的应用,以及二进制操作如不同位数计数和1的位计数的不同方法。
摘要由CSDN通过智能技术生成

1.递归打印一个数的每一位

思想:首先要确保每个数>9 才可以进行下一步操作

我们先定义一个变量存放 n%10  再进行递归下一步 最后进行打印

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

prin(int n)

{

  if (n == 0)

    return ;

  int bbb = n % 10;

  prin(n / 10);

  printf("%d", bbb);

}

int main() 

{

  int num = 12345;

  

  prin(num);

  printf("\n");

  return 0;

 2.递归和非递归分别实现求n的阶乘(不考虑溢出的问题)

 1.非递归 采用循环即可

注意:我们在定义函数类型时,由于阶乘较大 我们采用long long整形

long long Fac(int N)
{
    long long ret = 1;
    for(int i = 2; i <= N; ++i)
    {
        ret *= i;
    }
    
    return ret;
}

2. 递归 首先进行判断是否大于1  后进行递归 return Fac(N-1)*N

 

Fac(N)
        Fac(N-1)*N    N >= 2
*/


long long Fac(int N)
{
    if(N <= 1)
        return 1;
    
    return Fac(N-1)*N;
}

 

3.递归和非递归分别实现strlen

strlen的含义是:求字符串中有效字符的长度,不包括\0。以下是循环实现
int my_strlen(cahr* str)
{
  int count = 0;
  
  while('\0' != *str)
{

  count++;
  str++


}

return count;

 }

递归实现

int my_strlen(char *str)
{
	if('\0' == *str)
		return 0;
	else
		return 1+my_strlen(1+str);
}

 4.字符串逆序(递归实现)

思想:逆置字符串方法很简单 先给两个指针 分别指向首元素地址和末尾元素地址  交换两个指针上的字符  left指针向前走  right指针向右走  相遇后 逆置结束  

void reverse_string(char* string)
{
  char* left = arr;
  char* right = arr+strlen(arr)+1;


   while(left<right)
{
  char tmp = *left;
       *left = * right;
       *right = tmp;
      left++;
      right--;
}
}

 

递归方式:
对于字符串“abcdefg”,递归实现的大概原理:
  1. 交换a和g,
  2. 以递归的方式逆置源字符串的剩余部分,剩余部分可以看成一个有效的字符串,再以类似的方式逆置
void reverse_string(char* arr)
{
	int len = strlen(arr);
	char tmp = *arr;
	*arr = *(arr+len-1);
 
	*(arr+len-1) = '\0';
	if(strlen(arr+1)>=2)
		reverse_string(arr+1);
 
	*(arr+len-1) = tmp;
}

5.计算一个数的每位之和(递归实现)

思路:
            n    n < 10
DigiSum(n) = 
           DibiSum(n/10)+n%10   // 前n-1位之和+第N位
int DigitSum(int n)//1729
{
	if(n>9)
		return DigitSum(n/10)+n%10;
	else
		return n;
}

 6.递归实现n的k次方

思想: 如果为0 任何数的0次方==1  如果为整数  n的k次方等于n*n的(k-1)ci方进行递归

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

double power(double n, int k) {

  if (k == 0) {

    // 任何数的0次方都等于1

    return 1.0;

  }

  else if (k > 0) {

    // 如果k为正数,n的k次方等于n乘以n的(k-1)次方

    return n * power(n, k - 1);

  }

  else {

    // 如果k为负数,n的k次方等于1除以n的-k次方

    return 1.0 / (n * power(n, -k - 1));

  }

}



int main() {

  double n;

  int k;



  printf("请输入底数n:");

  scanf("%lf", &n);

  printf("请输入指数k:");

  scanf("%d", &k);



  double result = power(n, k);

  printf("%.2lf 的 %d 次方结果是 %.2lf\n", n, k, result);



  return 0;

}

7.计算斐波那契数


long long Fac(int N)
{
 if(N < 3)
     return 1;
 
 return Fac(N-1) + Fac(N-2);
}

 8.求两个数二进制中不同位的个数

编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同? 

输入例子:

1999 2299

输出例子:7

思想:首先有32个比特位 我们可以利用for循环进行遍历   将m向右移动一步和1进行按位与(&)

因为1的二进制是    00000000000000000000000000000001

m每向右移动一次和1进行按位与  有0则0  此时我们将n也进行右移按位与

如果m和n按位与后的结果不同 哦我们就创建一个变量统计数据

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int ocunt_diff(int m, int n)

{

	int count = 0;

	int i = 0;

	for (i=0; i < 32; i++)

	{

		if (((m >> i) & 1) !=( (n >> i) & 1))

			count++;

	}

	return count;

 }

int main()

{

	int m = 0;

	int n = 0;

	scanf("%d %d", &m, &n);

	int ret = ocunt_diff(m, n);

	printf("%d\n", ret);

	return 0;

}

 9.获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

思路:
1. 提取所有的奇数位,如果该位是1,输出1,是0则输出0
2. 以同样的方式提取偶数位置


 检测num中某一位是0还是1的方式:
   1. 将num向右移动i位
   2. 将移完位之后的结果与1按位与,如果:
      结果是0,则第i个比特位是0
      结果是非0,则第i个比特位是1
void Printbit(int num)
{
	for(int i=31; i>=1; i-=2)
	{
		printf("%d ", (num>>i)&1);
	}
	printf("\n");
    
	for(int i=30; i>=0; i-=2)
	{
		printf("%d ", (num>>i)&1);
	}
	printf("\n");
}

10.统计二进制中1的个数

方法一:
思路:
循环进行以下操作,直到n被缩减为0:
   1. 用该数据模2,检测其是否能够被2整除
   2. 可以:则该数据对应二进制比特位的最低位一定是0,否则是1,如果是1给计数加1
   3. 如果n不等于0时,继续1
int NumberOf1(int n)
{
	int count = 0;
	while(n)
	{
		if(n%2==1)
			count++;
		n = n/2;
	}
	return count;
}
上述方法缺陷:进行了大量的取模以及除法运算,取模和除法运算的效率本来就比较低。
方法二思路:
一个int类型的数据,对应的二进制一共有32个比特位,可以采用位运算的方式一位一位的检测,具体如下
int NumberOf1(unsigned int n)
{
	int count = 0;
	int i = 0;
	for(i=0; i<32; i++)
	{
		if(((n>>i)&1) == 1)
			count++;
	}
	return count;
}

 

方法二优点:用位操作代替取模和除法运算,效率稍微比较高
  缺陷:不论是什么数据,循环都要执行32次
  
方法三:
思路:采用相邻的两个数据进行按位与运算
举例:
9999:‭10 0111 0000 1111‬
第一次循环:n=9999   n=n&(n-1)=9999&9998= 9998
第二次循环:n=9998   n=n&(n-1)=9998&9997= 9996
第三次循环:n=9996   n=n&(n-1)=9996&9995= 9992
第四次循环:n=9992   n=n&(n-1)=9992&9991= 9984
第五次循环:n=9984   n=n&(n-1)=9984&9983= 9728
第六次循环:n=9728   n=n&(n-1)=9728&9727= 9216
第七次循环:n=9216   n=n&(n-1)=9216&9215= 8192
第八次循环:n=8192   n=n&(n-1)=8192&8191= 0


可以观察下:此种方式,数据的二进制比特位中有几个1,循环就循环几次,而且中间采用了位运算,处理起来比较高效
int NumberOf1(int n)
{
	int count = 0;
	while(n)
	{
		n = n&(n-1);
		count++;
	}
	return count;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hqxnb666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值