C

208 阅读7分钟

省略了#include <stdio.h>

没有main函数则表示所有代码放在main函数中

闰年

闰年条件: 能被4整除并且不能被100整除, 或者能被400整除的数为闰年

(year % 4 == 0 && year % 100 != 0) || year % 400 == 0)

一元二次方程

一般式:

ax2+bx+c=0(a0)ax^2 + bx + c = 0 (a \not= 0)
Δ=b24ac\Delta = b^2 - 4ac

Δ >= 0 时, 方程有两个实根

x=b±Δ2ax=\frac{-b\pm \sqrt{\Delta}}{2a}

Δ < 0 时, 方程没有实根

n的阶乘

n! = 1 * 2 * 3 * ... * n - 1 * n

5! = 1 * 2 * 3 * 4 * 5

n! = (n - 1)! * n

用循环:

long fac(int n) {
  long r = 1;
  while (n > 1) {
    r *= n;
    n--;
  }
  return r;
}

用递归:

long fac(int n) {
  if (n == 1) {
    return 1;
  } else {
    return fac(n - 1) * n;
  }
}

Fibonacci数列

斐波那契数列指的是这样一个数列:

1, 1, 2, 3, 5, 8, 13, 21...

这个数列从第3项开始,每一项都等于前两项之和

用循环:

int a = 1,    	// 上上项
		b = 1,      // 上一项
		c,          // 当前项
		n = 20;     // 设n为20
printf("%d %d ", a, b);
for (int i = 3; i <= n; i++) {
  c = a + b;		// 此轮当前项 = 前两项之和
  a = b;        // 下一轮上上项 = 此轮上一项
  b = c;        // 下一轮上一项 = 此轮当前项
  printf("%d ", c);
}

数组+循环:

int arr[20] = { 1, 1 };							// 前两位都是1
int i;
for (i = 2; i <= 20; i++) {					// 从第2位开始
  arr[i] = arr[i - 2] + arr[i - 1];	// 每一位 = 前两位之和
}
// 输出Fibonacci数列
for (i = 0; i < 20; i++) {
  printf("%d ", arr[i]);
}

用递归:

long fib(int n) {
  if (n == 1 || n == 2) {
    return 1;
  } else {
    return fib(n - 1) + fib(n - 2);
  }
}
void main() {
  int n = 20;
  for (int i = 1; i <= n; i++) {
    printf("%ld ", fib(i));
  }
}

99乘法表

1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81

int i, j;
for (i = 1; i <= 9; i++) {
  for (j = 1; j <= i; j++) {
    printf("%d*%d=%d\t", j, i, j * i);
  }
  printf("\n");
}

搬砖问题

有36块砖, 需要36人搬运, 男人每人搬4块, 女人每人搬3块, 儿童两人搬1块

男人取值范围为0 ~ 36 / 4 => 0 ~ 8

女人取值范围为0 ~ 36 / 3 => 0 ~ 12

儿童取值范围为0 ~ 36 => 0 ~ 36, 且为偶数

int man, woman, child;
for (man = 0; man <= 8; man++) {
  for (woman = 0; woman <= 12; woman++) {
    for (child = 0; child <= 36; child += 2) {
      if ((4 * man + 3 * woman + child * 0.5 == 36) && (man + woman + child == 36)) {
        printf("men: %d\n", men);
        printf("women: %d\n", women);
        printf("childs: %d\n", childs);
      }
    }
  }
}

1~100的素数

素数: 除了1和它本身不能被其他数整除的数

int isPrime(int n) {
  int i, t = sqrt(n); // 开根号是因为, 自t开始, 后面的步骤都是多余的, 自行推算
  for (i = 2; i <= t; i++) { // 从2遍历到t
    if (n % i == 0) { // 如果可以整除, 则不是素数
      return 0;
    }
  }
  return 1; // 否则是素数
}
void main() {
  int i;
  for (i = 1; i <= 100; i++) {
    if (isPrime(i)) { // 对1~100的数逐个判断
      printf("%d ", i);
    }
  }
}

输出所有的水仙花数

水仙花数是指一个3位数,其各位数字立方和等于该数本身

eg: 153 = 1^3 + 5^3 + 3^3

int i, hundred, ten, indiv;
for (i = 100; i < 1000; i++) { // 遍历所有的三位数
  hundred = (int)i / 100, // 百位
  ten = (int)i / 10 % 10,	// 十位
  indiv = i % 10;					// 个位
  // 如果其各位数字立方和等于它本身, 则为水仙花数
  if (i == hundred * hundred * hundred + ten * ten * ten + indiv * indiv * indiv) {
    printf("%d ", i);
  }
}

印度国王的奖励

在国际象棋棋盘上放麦子, 第一格放1粒, 第二格放2粒, 第三格放4粒, 每个格子的麦子都按前一个格子的2倍计算. 需要64格的麦子. 一共需要多少粒麦子?

int i;
double s = 0, 
			 t = 1; // 第一次放1粒麦子
for (i = 1; i <= 64; i++) {	// 放麦子的次数为64次
  s += t; // 将这一轮的麦子累加到s里
  t *= 2; // 下一格的麦子 = 这一格的麦子 * 2
}
printf("%e\n", s); // 最后用科学计数法输出一共需要多少粒麦子

按照公式计算s的值

s=1+11+2+11+2+3+++11+2+3++ns = 1 + \frac{1}{1 + 2} + \frac{1}{1 + 2 + 3} + \cdots + + \frac{1}{1 + 2 + 3 + \cdots + n}

每一项的分母也是一个累加求和问题

n项的分母是前n-1项的分母 + n

float s = 0; // s初值为0
int i,
		n = 5, // 设n为5
		t = 0; // 分母初值为0
for (i = 1; i <= n; i++) { // 以i代表n, 遍历n次
  t += i; // 将分母累加起来(第i项的分母 = 上一项的分母 + i)
  s += 1.0 / t;
}
printf("%f", s);

交换变量

void swap(int *p1, int *p2)
{
  int t = *p1;
  *p1 = *p2;
  *p2 = t;
}

冒泡排序

每次将相邻的两个数比较, 将大的数调到后面

如果有 n 个数,则要进行 n - 1 轮比较, 第 i 轮比较中要进行 n - 1 - i 次比较

先定义一个用来交换变量的函数

void swap(int* p1, int* p2) {
  int t = *p1;
  *p1 = *p2;
  *p2 = t;
}

不使用指针遍历:

void sort(int arr[], int len) {
  int i, j, temp, current, next;
  for (i = 1; i < len; i++) {
    for (current = 0; current < len - i; current++) {
      next = current + 1;
      if (arr[current] > arr[next]) {
        swap(&arr[current], &arr[next]);
      }
    }
  }
}

使用指针遍历:

void sort(int arr[], int len) {
  int* current, 
  	 * next, 
  		 temp, 
  		 i;
  for (i = 1; i < len; i++) {
    for (current = arr; current < arr + len - i; current++) {
      next = current + 1;
      if (*current > *next) {
        swap(current, next);
      }
    }
  }
}

二分查找

在升序数组中查找target

begin为首位, end为末位, middle为中位

middle = (begin + end) / 2

  1. if (target == arr[middle]) 查找成功;

    else if(target > arr[middle]) begin = middle + 1;

    else if(target < arr[middle]) end = middle - 1;

  2. if(begin > end) 查找失败

// 查找成功返回对应索引值, 查找失败返回-1, target为要查找的值
int search(int target, int arr[], int len) {
  int begin = 0,			// 首位初始值为0
  		end = len - 1,	// 末位初始值为数组长度 - 1
  		middle;					// 中位暂不设值
  if (target < arr[begin] || target > arr[end]) { // 如果target比首位小或比末位大
    return -1;																		 // 查找失败
  }
	// 经上述步骤后, 确定target >= arr[begin] && target <= arr[end]
  // ∴ target在arr[begin] ~ arr[end]的范围内, 但并不一定存在于数组中, 现在开始二分查找
  do {
    middle = (begin + end) / 2;		// 确定中位
    if (target == arr[middle]) {	// 如果target等于中位值
      return middle;							// 则查找成功, 返回索引值
    } else if (target < arr[middle]) {	// 如果目标值小于中位值
      end = middle - 1; 								// 确定前半段为下一次查找的范围
    } else {														// 如果目标值大于中位值
      begin = middle + 1;								// 确定前半段为下一次查找的范围
    }
  } while (begin <= end); // 当begin > end时, 数组中不存在target
  return -1;							// 查找失败
}

统计字符串中的单词

#include <string.h>
int countWords(char str[]) {
  int len = strlen(str), 	// 字符串长度
  		previous, 					// 用来表示字符串中的前一位字符
  		current, 						// 用来表示字符串中的当前位字符
  		words = 0; 					// 单词数量默认为0
  if (str[0] != '\0') { 	// str不是空字符串时开始统计
    if (str[0] != ' ') { 	// 首字符是非空格, 置单词数为1
      words = 1;
    }
    // 从字符串第1位开始遍历, 直到结束
    for (current = 1; str[current] != '\0'; current++) {
      previous = current - 1;
      // 前一位是空格, 当前位不是空格, 表示出现新单词
      if (str[previous] == ' ' && str[current] != ' ') {
        words++;
      }
    }
  }
  return words;
}

使用指针:

int countWords(char str[]) {
  char* previous,				 // 用来表示字符串中的前一位字符
    	* current = str;	 // 用来表示字符串中的当前位字符
  int 	words = 0;				 // 单词数量默认为0
  if (*current != '\0') {		// str不是空字符串时开始统计
    if (*current != ' ') {	// 首字符是非空格, 置单词数为1
      words = 1;
    }
    // 将current++, 也就是从字符串第1位开始遍历, 直到结束
    for (current++; *current != '\0'; current++) {
      previous = current - 1;
      // 前一位是空格, 当前位不是空格, 表示出现新单词
      if (*previous == ' ' && *current != ' ') {
        words++;
      }
    }
  }
  return words;
}

矩阵求和

1 2 1 2 2 4

3 4 + 3 4 => 6 8

5 6 5 6 10 12

#define M 3
#define N 2
int a[M][N] = { { 9 , -16 }, { 6,   21 }, { 25, 18 } },
		b[M][N] = { { 16,  89 }, { 26, -27 }, { 36, 81 } },
		i,
		j,
		c[M][N];
for (i = 0; i < M; i++) {
  for (j = 0; j < N; j++) {
    c[i][j] = a[i][j] + b[i][j];
  }
}

矩阵转置

1 2 1 3 5

3 4 =>

5 6 2 4 6

#define M 3
#define N 2
int a[M][N] = { { 1, 2 }, { 3, 4 }, { 5, 6 } },
		b[N][M];
for (int i = 0; i < M; i++) {
  for (int j = 0; j < N; j++) {
    b[j][i] = a[i][j];
  }
}

求矩阵最大值

#define M 3
#define N 2
int a[M][N] = {
  { 1, 2 },
  { 3, 4 },
  { 5, 6 }
};
int i,
		j,
  	row = 0				 // 行号
  	col = 0,			 // 列号
		max = a[0][0]; // 最大值默认为a[0][0]
for (i = 0; i < M; i++) {
  for (j = 0; j < N; j++) {
    if (a[i][j] > max) {
      max = a[i][j]; // 将更大的值保存在max中
      row = i; // 记录行号
      col = j; // 记录列号
    }
  }
}
printf("max: a[%d][%d]=%d\n", row, col, max);

求M*M矩阵对角线元素之和

M为偶数时: a[i][i] + a[i][M - i - 1]

M为奇数时: a[i][i] + a[i][M - i - 1] - a[M / 2][M / 2]

#define M 3
int a[M][M] = {
  { 1, 2, 3 },
  { 4, 5, 6 },
  { 7, 8, 9 }
};
int sum = 0,
		i;
for (i = 0; i < M; i++) {
  sum += a[i][i] + a[i][M - i - 1];
}
if (M % 2 == 1) { 				// 判断a是奇数矩阵还是偶数矩阵
  sum -= a[M / 2][M / 2]; // a是奇数矩阵时, 将重复累加的中心元素值扣除
}
printf("Sum = %d\n", sum);

杨辉三角

​ 1

​ 1 1

​ 1 2 1

​ 1 3 3 1

1 4 6 4 1

1 5 10 10 5 1

首尾都是1

从第2行开始每一行的1位到倒数第二位的规律: **当前行当前列 = 上一行上一列 **+ 上一行当前列

arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j]

#define N 6
int arr[N][N], 
		i,
		j;
for (i = 0; i < N; i++) {
  arr[i][0] = 1;
  arr[i][i] = 1;
}
for (i = 2; i < N; i++) {
  for (j = 1; j < i; j++) {
    arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
  }
}

数组反转

void swap(int* p1, int* p2) {
  int t = *p1;
  *p1 = *p2;
  *p2 = t;
}
void reverse(int arr[], int len) {
  int i,
    	middle = len / 2;
  for (i = 0; i < middle; i++) {
    swap(arr + i, arr + (len - i - 1));
  }
}

将电文转译成密码

将一个字符串中的

"abcdefghijklmnopqrstuvwxyz "

转译为

"0123456789abcdefghi@#$%&!*j"

其他字符不变

// 查找字符串中的字符, 成功返回索引, 失败返回-1
int search(char str[], char c) {
  int i;
  for (i = 0; str[i] != '\0'; i++) {
    if (str[i] == c) {
      return i;
    }
  }
  return -1;
}
void main() {
  char code1[] = "abcdefghijklmnopqrstuvwxyz ", // 明文表
  		 code2[] = "0123456789abcdefghi@#$%&!*j", // 密文表
  		 str1[] = "This is a example.",						// 要转译的明文
  		 str2[50];																// 转译后的密文
  int i,
  		index;
  for (i = 0; str1[i] != '\0'; i++) { // 遍历明文表
    if ((index = search(code1, str1[i])) != -1) { // 在明文表中逐个查找明文中的每一个字符
      str2[i] = code2[index];	// 查找成功则向str2中与str1对应的位置赋值为对应的密文字符
      continue; // 然后跳过此轮循环, 准备处理下一个字符
    }
    str2[i] = str1[i]; // 查找失败则直接向str2中对应的位置赋值为明文字符
  }
  str2[i] = '\0'; // 最后结束字符串
  puts(str2);
}

汉诺塔问题

有ABC三个柱子, n个大小不相同的盘子, 所有盘子以塔状叠放在A柱上, 按照一定规则将A柱的所有盘子移动到B柱上, C柱为中转柱, 移动规则如下:

  1. 一次只能移动一个盘子
  2. 始终保持小盘在上, 大盘在下

步骤:

若有n个盘子

  1. 把A上面的n - 1个盘子移到C
  2. 将A仅有的一个盘子(也是最大的一只)直接移到B上
  3. 将C上的n - 1个盘子移到B上, 如此反复(递归)
void hanoi(int n, char A, char B, char C) {
  if (n == 1) {
    printf("%c-->%c", A, B);			// 在递归过程中, 每当只剩1个盘子时,直接移动到目标盘
    return;
  } else {
    hanoi(n - 1, A, C, B);				// 第一步: 将上面的n-1个盘子由A经过B, 移到C
    printf("\t%c-->%c\t", A, B);  // 第二步: 将A上仅有的1个盘子直接移到B
    hanoi(n - 1, C, B, A);				// 第三步: 将C上的n-1个盘子由C经过A, 移到B
  }
}
void main() {
  hanoi(3, 'A', 'B', 'C');
}

最大公约数和最小公倍数

最大公约数:

辗转相除法: 将除数和余数反复做除法运算, 当余数为0时, 取算式中的除数为最大公约数

eg : 28和21

28 % 21 = 1 ... 7

21 % 7 = 3 ... 0

最大公约数为7

最小公倍数:

∵两个自然数的乘积 = 最大公约数 * 最小公倍数

∴最小公倍数 = 两个自然数的乘积 / 最大公约数

= 28 * 21 / 7

= 84

最小公倍数为84

int getGcd(int m, int n) {	// 被除数m, 除数n
  int r;										// 余数
  while ((r = m % n) != 0) {
    m = n; 	// 下一轮的被除数为这一轮的除数
    n = r;	// 下一轮的除数为这一轮的余数
  }
  return n; // 当余数为0时, 此时的除数为最大公约数
}
int getLcm(int m, int n, int gcd) {
  return m * n / gcd; // 最小公倍数 = 两数乘积 / 最大公约数
}
void main() {
  int m, n;
  scanf("%d %d", &m, &n);
  int gcd = getGcd(m, n),
    	lcm = getLcm(m, n, gcd);
  printf("%d %d\n", gcd, lcm);
}

判断传入的数是否是素数()

int isPrime(int n) {
  int flag = 1, i, t = sqrt(n);
  for (i = 2; i <= t; i++) {
    if (n % i == 0) {
      flag = 0;
      break;
    }
  }
  return flag;
}

字符串排序

通过交换地址的方式排序字符串数组

#include <string.h>
void sort(char* strArr[], int len) {
  char* temp;						// 指向字符串
  int i, 
  		current,
  		next;
  for (i = 1; i < len; i++) {
    for (current = 0; current < len - i; current++) {
      next = current + 1;
      // 如果这一位字符串大于下一位, 则交换指向字符串的指针的地址
      if (strcmp(strArr[current], strArr[next]) > 0) {
        temp = strArr[current];
        strArr[current] = strArr[next];
        strArr[next] = temp;
      }
    }
  }
}

三色球问题

有红、黄、蓝、白、黑五种颜色的球若干个, 每次取出三个球 求出所有三种不同颜色的球的可能取法

int i, 
		j, 
		k, 
		n = 1;
char* colors[5] = { "red", "yellow", "blue", "white", "black" };
for (i = 0; i < 5; i++) {
  for (j = 0; j < 5; j++) {
    for (k = 0; k < 5; k++) {
      if (i != j && j != k && i != k) {
        printf("%d\t%s\t%s\t%s\n", n++, colors[i], colors[j], colors[k]);
      }
    }
  }
}

从指针数组中查找第一个包含子串的字符串()

#include <string.h>
char* findFirstStrIncludesubStrFromStrArr(char* strArr[], char* subStr, int len) {
  int i;
  char* str;
  for (i = 0; i < len; i++) {
    str = strArr[i];
    if (strstr(str, subStr) != 0) {
      return str;
    }
  }
  return '\0';
}

文本文件的复制

#include <stdio.h>
void main(int argc, char* argv[]) {
  char* source = argv[1], * target = argv[2], c;
  FILE* sourceP, * targetP;
  if ((sourceP = fopen(source, "r")) == NULL) {
    printf("cannot open source file");
    exit(0);
  }
  if ((targetP = fopen(target, "w")) == NULL) {
    printf("cannot open target file");
    exit(0);
  }
  while (!feof(sourceP) && (c = fgetc(sourceP)) != EOF) {
    fputc(c, targetP);
  }
  fclose(sourceP);
  fclose(targetP);
}

用位运算使一个整型数据(4字节)清零

#include <stdio.h>
void main() {
  int i = 1234;
  i = i & 0x00000000;
}

用位运算输出整型数据(4字节)的高字节和低字节

#include <stdio.h>
void main() {
  int i = 1234, high, low;
  low = i & 0000ffff; // 取低字节
  high = (i >> 16) & 0000ffff; // 取高字节
}

函数, 测定整数i(4字节)的最高位, 为0则输出"正数", 为1则输出"负数"

void test(int i) {
  if (i & 0x80000000) {
    printf("负数");
  } else {
    printf("正数");
  }
}

函数, 使一个整数i的二进制形式中的低4位改为1, 并将得到的数返回

int f(int i) {
  int t = i | 0x0000000f;
  return t;
}

函数, 将一个整数i(4字节)循环左移4位

int f(int i) {
 int a = (i >> (32 - 4)) & 0x0000000f; 	// a的值是i最左边的4位
 int b = i << 4;												// b的值是i左移4位后的结果
 int c = (a | b) & 0xffffffff;					// 与运算拼接
 return c;
}

函数, 取一个整数最高端的5个二进制位

int f(int i) {
  int c = i >> (32 - 5);
  c = c & 0x0000001f;
  return c;
}

所有的文件函数

FILE* fopen(char* filename, char* mode)
fclose(FILE* fp)
int fgetc(FILE* fp)
fputc(int c, FILE* fp)
fread(void* buffer, int size, int count, FILE* fp)
fwrite(void* buffer, int size, int count, FILE* fp)
int feof(FILE* fp)
long ftell(FILE* fp)
fseek(FILE* fp, int offset, int origin)
rewind(FILE* fp)
fscanf(FILE* fp, char* format, ...)
fprintf(FILE* fp, char* format, ...)

动态单向链表

#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct Student)
struct Student {
  long num;
  float score;
  struct Student* next;
};
int n;
struct Student* create() {
  struct Student* head;
  struct Student* p1, * p2;
  n = 0;
  p1 = p2 = (struct Student*)malloc(LEN);
  scanf("%ld,%f", &p1->num, &p1->score);
  head = NULL;
  while (p1->num != 0) {
    n++;
    if (n == 1) head = p1;
    else p2->next = p1;
    p2 = p1;
    p1 = (struct Student*)malloc(LEN);
    scanf("%ld,%f", &p1->num, &p1->score);
  }
  p2->next = NULL;
  return head;
}
void print(struct Student* head) {
  struct Student* p;
  printf("\nNow, These %d records are:\n", n);
  p = head;
  if (head != NULL) {
    do {
      printf("%ld\t%5.1f\n", p->num, p->score);
      p = p->next;
    } while (p != NULL);
  }
}
int main() {
  struct Student* head;
  head = create();
  print(head);
  return 0;
}