C语言编程进阶精选题
一、数据存储
1.1 十进制转二级制
#include <stdio.h>
int main(){
int a[32];
int n,i=0;
scanf("%d",&n);
//处理特殊边界
if(n=0){
printf("0");
}
//取余法转二进制
while(n>0){
a[i]=n%2;
n/=2;
i++;
}
//逆序输出余数
for(i--;i>=0;i--){
printf("%d",a[i]);
}
return 0;
}
十进制转二进制的步骤如下:
取余法:将十进制数不断除以 2,记录每次的余数(0 或 1)。
例如:17 ÷ 2 = 8 余 1
重复此步骤直到商为 0。
逆序排列余数:将记录的余数按逆序排列,得到最终的二进制数。
例如:17 的余数依次为 1,0,0,0,1 → 逆序后为 10001。
数学原理:
二进制每一位对应 2 的幂次(如 2⁰, 2¹, 2²…)。每次除以 2 相当于逐位确定每一位的权值,余数即为当前位的数值。
1.2 浮点数转二级制
#include <stdio.h>
void floatToBinary(float f) {
int i;
printf("0.");
for(i=0;i<32;i++){
f*=2;
if(f>=1){ //只看整数部分决定取0还是取1
printf("1");
f-=1; //只保留小数部分
}
else{
printf("0");
}
if(f==0){ //小数部分为0结束
break;
}
}
}
int main() {
float f;
scanf("%f", &f);
floatToBinary(f);
floatToBinary(f);
return 0;
}
十进制小数转换为二进制小数采用 “乘 2 取整,顺序排列” 法,具体步骤如下:
初始运算:用 2 乘以十进制小数,得到一个积。
取出整数部分:将积的整数部分取出,这个整数部分只能是 0 或者 1,它将作为二进制小数的一位数字 。
判断小数部分:检查积的小数部分,如果小数部分为 0,转换结束;如果小数部分不为 0,则将该小数部分作为新的被乘数,返回步骤 1 继续运算。
排列结果:将每次取出的整数部分,按照取出的先后顺序从左到右排列,得到的数字序列就是对应的二进制小数。
需要注意的是,有些十进制小数不一定能完全准确地转换成二进制小数,会出现无限循环或无限不循环的情况。在这种情况下,可以根据精度要求,转换到小数点后指定的位数即可。
案例如下:
1.3 二进制转十进制
#include <stdio.h>
#include <string.h>
// 二进制转十进制
int binaryToDecimal(char str[]) {
int sum=0,weight=1;
int pos=strlen(str)-1;
//从后往前,从低位到高位
for(;pos>=0;pos--){
sum+=(str[pos]-'0')*weight;
weight*=2; //不断增加权重
}
return sum;
}
int main() {
char Str[32];
scanf("%s", &Str);
printf("%d", binaryToDecimal(Str));
return 0;
}
将二进制数的每一位按权值展开并求和,实现了二进制到十进制的转换
1.4 大整数加🚀🚀🚀
#include <stdio.h>
#include <string.h>
int main() {
//用来接收用户输入的字符串形式的大整数
char sNum1[201] = {'\0'};
char sNum2[201] = {'\0'};
//存放相加后的结果方便从高位到低位输出
//当作1字节的int型使用节省内存
char result[201];
gets(sNum1); gets(sNum2);
int len1 = strlen(sNum1), len2 = strlen(sNum2);
int addition = 0, push = addition / 10; //用于进位
int reslen = 0;
//注意处理长度不一的情况
for(int i = len1 - 1, j = len2 - 1 ; i >= 0 || j >= 0; i--, j--){
//从低位到高位依次相加,当i或j小于0时说明改为0
char a = ( i < 0 ? '0' : sNum1[i] );
char b = ( j < 0 ? '0' : sNum2[j] );
addition = a - '0' + b - '0' + push;
result[reslen++] = addition % 10;
push = addition / 10; //下一个高位是否需要进位
}
//最高位的进位特殊处理
if(push == 1){
result[reslen++] = 1;
}
for(int i = reslen - 1; i >= 0; i--)
printf("%d", result[i]);
return 0;
}
二、遍历和循环
2.1 数组循环右移
#include <stdio.h>
int main() {
int a[100];
int n,step,i;
scanf("%d",&n);
for(i=0;i<n;i++){ //输入数组
scanf("%d",&a[i]);
}
scanf("%d",&step);
for(i=n-step;i<n;i++){ //先把右移步数大小,后半部分输出
printf("%d ",a[i]);
}
for(i=0;i<n-step;i++){ //在输出剩下的
printf("%d ",a[i]);
}
return 0;
}
方法二:
#include <stdio.h>
// 实现数组元素整体循环右移的函数
void rightRotate(int arr[], int n, int steps) {
int temp[n];
for (int i = 0; i < n; i++) // 将数组元素整体右移steps步存储到临时数组中
temp[(i + steps) % n] = arr[i];
for (int i = 0; i < n; i++) // 将临时数组中的元素复制回原数组
arr[i] = temp[i];
}
int main() {
int n, steps;
scanf("%d", &n);
int arr[n];
for (int i = 0; i < n; i++)
scanf("%d", &arr[i]);
scanf("%d", &steps);
rightRotate(arr, n, steps);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}
2.2 左大右小(分治)🚀🚀
#include <stdio.h>
#include <stdlib.h>
//把下标为index的元素左移(左移到基准左边)
void moveToLeft(int arr[], int index, int step) {
int temp = arr[index]; //index移动步数范围整体左移,会把基准给覆盖掉,所以先保存!
for(int i = index; (index - i) < step && i >= 1; i--){
arr[i] = arr[i-1];
}
arr[index - step] = temp;
}
void Partition(int arr[], int n) {
int index = 1;
int pivot = arr[0];
int rightNum = 0; //记录大于pivot的元素个数
while(index < n) {
if(arr[index] <= pivot)
moveToLeft(arr, index, rightNum + 1);
else
rightNum++; //因为比基准的不移动,在遍历到比基准小的数移动时,要跨过那些没有移动的大数!🎈
index++;
}
}
int main() {
int arr[100];
int n = 0;
char ch;
// 读取一系列正整数数据,直到遇到回车符
while (scanf("%d%c", &arr[n], &ch) == 2) {
n++;
if (ch == '\n')
break;
}
Partition(arr, n);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}
这段代码的核心思想是 基于基准值的原地元素调整,具体如下:
- 基准值设定
以数组第一个元素 arr[0] 作为基准值 pivot,后续元素根据与 pivot 的大小关系调整位置。- 遍历与分类处理
遍历数组(从第二个元素开始),若元素 arr[index] <= pivot,则通过 moveToLeft 函数将其左移,确保这些元素按相对顺序聚集到数组左侧。
若元素 arr[index] > pivot,则记录其数量(rightNum++),后续通过左移操作,将这些元素 “挤” 到基准值右侧。- 元素左移实现
moveToLeft 函数负责将指定元素左移。通过覆盖移动的方式,在不开辟额外数组空间的前提下,调整元素位置,保证 “小于等于基准值” 的元素按原始相对顺序排列在左侧。- 最终划分
遍历结束后,数组实现 “左小右大” 的划分:左侧是小于等于基准值的元素(相对顺序不变),右侧是大于基准值的元素(相对顺序不变),基准值位于左右两部分之间。
整体属于 自定义的原地划分策略,通过元素移动而非额外空间,实现特定规则下的数组重组,同时保留元素相对次序。
方法二思路:
用两个数组实现,把比基准小的放一个数组,比基准大的放一个数组,然后逐一输出小的数组→基准→大的数组
2.3 判断上三角
#include <stdio.h>
#include <stdlib.h>
// 判断是否为上三角矩阵的函数
int isupperTriangularMatrix(int mat[][10], int n) { //注意行参写法
int i,j;
for (i = 1; i < n; i++)
for (j = 0; j < i; j++)
if (mat[i][j] != 0)
return 0; // 不是上三角矩阵
return 1; // 是上三角矩阵
}
int main() {
int n,i,j;
// 输入方阵维度
scanf("%d", &n);
int matrix[10][10];
// 输入矩阵元素
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 调用判断函数
if (isupperTriangularMatrix(matrix, n)) {
printf("YES"); // 输出YES
} else {
printf("NO"); // 输出NO
}
return 0;
}
2.4判断数根
#include <stdio.h>
#include <stdlib.h>
//返回n的各位数字之和
int digitSum(int n){
int sum=0;
while(n>0){
sum+=n%10;
n/=10;
}
return sum;
}
int main(){
int n;
scanf("%d", &n);
while ( n >= 10 ){
n = digitSum(n);
}
printf("%d", n);
return 0;
}
三、排序思想qsort
3.1田忌赛马qsort函数 🚀🚀🚀
#include <stdio.h>
#include <stdlib.h>
// 降序比较函数
int compare(const void* a, const void* b) {
return *(int*)b - *(int*)a;
}
int main() {
int N;
while (1) {
scanf("%d", &N);
if (N == 0) break;
int my_horses[N], opponent_horses[N];
// 读取自己的马
for (int i = 0; i < N; i++) {
scanf("%d", &my_horses[i]);
}
// 读取对手的马
for (int i = 0; i < N; i++) {
scanf("%d", &opponent_horses[i]);
}
// 降序排序
qsort(my_horses, N, sizeof(int), compare);
qsort(opponent_horses, N, sizeof(int), compare);
int win_count = 0;
int my_left = 0, my_right = N - 1;
int opp_left = 0, opp_right = N - 1;
while (my_left <= my_right) {
// 自己最快的马能赢对手最快的马,赢一场
if (my_horses[my_right] > opponent_horses[opp_right]) {
win_count++;
my_right--;
opp_right--;
}
// 自己最快的马赢不了对手最快的马,用最慢的马消耗对手最快的马
else {
// 自己最慢的马对比对手最快的马,输一场
if (my_horses[my_left] < opponent_horses[opp_right]) {
my_left++;
opp_right--;
}
// 平局情况(按规则算输),直接消耗
else {
my_left++;
opp_right--;
}
}
}
// 判断是否赢超过一半
if (win_count > N / 2) {
printf("yes\n");
} else {
printf("no\n");
}
}
return 0;
}
代码解析:
- 输入与排序:读取马的数量和速度,对双方马的速度降序排序,便于后续贪心比较。
- 双指针贪心策略:
my_left
(自己最慢的马)、my_right
(自己最快的马)。opp_left
(对手最慢的马)、opp_right
(对手最快的马)。- 优先用自己最快的马赢对手最快的马;若赢不了,用自己最慢的马消耗对手最快的马,尽可能保留强马赢更多局。
- 结果判断:统计胜利场数,若超过总场数一半,输出
yes
,否则输出no
。
qsort
是C语言标准库中的一个排序函数,使用快速排序算法对数组进行排序,位于头文件<stdlib.h>
中。下面介绍它的使用方法:
1. 函数原型
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));
2. 参数说明
base
:指向要排序的数组的第一个元素的指针,也就是数组的起始地址。nitems
:数组中元素的数量,例如数组int arr[10]
,这里nitems
就是10
。size
:每个元素的大小,以字节为单位。可以使用sizeof
运算符获取,如sizeof(arr[0])
。compar
:指向比较函数的指针,用于确定元素的顺序。比较函数由用户自定义,应接受两个const void *
类型的参数,返回一个整数值,具体规则如下:- 如果第一个参数小于第二个参数,则返回小于零的值。
- 如果两个参数相等,则返回零。
- 如果第一个参数大于第二个参数,则返回大于零的值。
3. 使用示例
- 对整数数组进行升序排序
#include <stdio.h>
#include <stdlib.h>
// 比较函数,用于升序排序
int compare_ints(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int main() {
int arr[] = {5, 2, 3, 1, 4};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(arr[0]), compare_ints);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
在这个示例中,compare_ints
函数将两个const void *
类型的参数强制转换为int *
类型,然后通过减法运算返回比较结果。qsort
函数根据这个比较结果对整数数组arr
进行排序。
- 对字符串数组进行排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 比较函数,用于按字典序升序排序字符串
int compare_strings(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
int main() {
const char *arr[] = {"welcome", "to", "geeks", "for", "geeks"};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(arr[0]), compare_strings);
for (int i = 0; i < n; i++) {
printf("%s ", arr[i]);
}
return 0;
}
对于字符串数组,比较函数compare_strings
需要将const void *
类型的参数强制转换为const char **
类型,然后使用strcmp
函数进行字符串比较。
- 对结构体数组进行排序
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int data;
} item;
// 比较函数,用于根据结构体中的数据成员进行升序排序
int compare_items(const void *a, const void *b) {
return ((item *)a)->data - ((item *)b)->data;
}
int main() {
item arr[] = {{5}, {2}, {3}, {1}, {4}};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(item), compare_items);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i].data);
}
return 0;
}
在对结构体数组排序时,compare_items
函数将const void *
类型的参数强制转换为item *
类型,然后根据结构体中的data
成员进行比较。
qsort
比较函数的返回值规则:- 当比较函数的返回值小于
0
时,qsort
认为第一个参数(a
所指向的元素)应该排在第二个参数(b
所指向的元素)前面。 - 当返回值等于
0
时,两个元素的相对顺序不确定,但都被视为相等。 - 当返回值大于
0
时,第一个参数(a
所指向的元素)应该排在第二个参数(b
所指向的元素)后面。
- 当比较函数的返回值小于
compare_ints
函数的运算逻辑:该函数中,a
和b
是const void *
类型的指针,通过(int*)
将它们强制转换为指向整数的指针,再使用*
解引用操作符获取指针指向的整数值。然后计算(*(int*)a - *(int*)b)
, 即第一个整数减去第二个整数:- 若
*(int*)a
小于*(int*)b
,比如*(int*)a
为3
,*(int*)b
为5
,那么(*(int*)a - *(int*)b)
的结果是-2
(小于0
) ,按照qsort
的规则,此时对应a
的元素会被排在对应b
的元素前面。 - 若
*(int*)a
等于*(int*)b
,例如都为4
,则(*(int*)a - *(int*)b)
结果是0
,这两个元素相对顺序不确定,但被视为相等。 - 若
*(int*)a
大于*(int*)b
,比如*(int*)a
为7
,*(int*)b
为2
,(*(int*)a - *(int*)b)
结果是5
(大于0
), 按照规则,对应a
的元素会被排在对应b
的元素后面。
- 若
在qsort
排序过程中不断调用这个比较函数,从而使得较小的整数逐渐排在前面,较大的整数逐渐排在后面,最终实现整数数组的升序排序。
3.2 最小乘积
#include <stdio.h>
#include <stdlib.h>
// 比较函数,用于qsort排序
int compare(const void *a, const void *b) {
return *(int *)a - *(int *)b;
}
int main() {
int n;
scanf("%d", &n);
int *arr1 = (int *)malloc(n * sizeof(int));
int *arr2 = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; i++)
scanf("%d", &arr1[i]);
for (int i = 0; i < n; i++)
scanf("%d", &arr2[i]);
// 对两个数组进行排序
qsort(arr1, n, sizeof(int), compare);
qsort(arr2, n, sizeof(int), compare);
int minProductSum = 0;
for (int i = 0; i < n; i++)
minProductSum += arr1[i] * arr2[n - i - 1];
printf("%d\n", minProductSum);
free(arr1);
free(arr2);
return 0;
}
贪心的思想 用小数和大数乘