C基础算法(个人整理版)
2011年5月,与WC和蓝总同去图书馆,突有此念头,故进行问题搜集并编写整理如下。部分代码参考或部分节选于网友程序。此为20个比较基础的,在各类C教程后习题中常见到的,后续将补充相关专业算法。
2011年5月,SeptStringS!
1, 两个值互换的问题
也就是两个变量分别存着两个数,现在没事找事的人要把这两个数互相调换一下。
首先的想法是,有两个变量,分别为a、b,拿水杯为例,假设a中装有可乐、b是雪碧,不考虑容积的话,就是再找一个杯子,它是路人甲temp。先把可乐倒到temp里面,然后把雪碧倒到a瓶子中,接着把可乐从temp倒回b杯子里,乾坤大挪移完成!!
#include "stdio.h"
void trans()
{
int a,b,temp;
printf("t输入两个整数:n");
scanf("%d%d",&a,&b);
printf("t交换之前a = %d b = %dn",a,b);
temp = a;
a = b;
b = temp;
printf("t交换之后a = %d b = %dn",a,b);
}
2, 计算某年是不是闰年
其实一直没明白这个题目为什么是每一本C教材必备的课后编程题,闰年嘛,口算一下就知道了……
计算的思路大概如下:年份是4的倍数且不是100的倍数,或者年份是400的倍数。
#include "stdio.h"
void main()
{
int year;
scanf("%d",&year);
if((year%4 == 0&&year0 != 0)||year@0 == 0)
{
printf("This year is a leap yearn");
}
else
{
printf("This year is not a leap yearn");
}
}
3, 冒泡法排序
有一堆数,在一个数组a[N]中。要把她们从最小到最大或者最大到最小依次序排列。
如果需要从小到大排列,基本的想法是,从第一个数开始,两两比较,然后把较大的数放在后面,依次推进,一轮操作之后,a[N-1]将是最大的数。接着,从a[0]到a[N-2]再进行同样的操作…………N-1轮之后,所有数排序整齐!
#include "stdio.h"
void selsort(int a[],int n)
{
int i,j,temp;
for(i = 0;i < n-1;i++)
{
for(j = 0;j < n-i-1;j++)
{
if(a[j] > a[j+1])//从小到大
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
4, 选择排序法
这个排序的方法比较好理解,一堆数,你找出最小那个放在第一个位置。然后在余下的数里面找出其中最小的,放在第二个位置。接着同理…………直到完成排序。同样,从大到小也是一个理。
#include "stdio.h"
void selsort(int a[],int n)
{
int i,j,temp,temp2;
for(i = 0;i < n-1;i++)
{
temp = a[i];
for(j = i+1;j < n;j++)
{
if(a[j] < temp)
{
temp2 = temp;
temp = a[j];
a[j] = temp2;
}
}
a[i] = temp;
}
}
5, 插入排序法
举个例子,学生要按个头排队,那么先捉一个出来站好,第二个如果比他高就站第一个后面,如果矮就站在前面,第二个站好后新的队伍形成。第三个要进来,就按照他的个头决定在一前面还是二前面或者二后面(假定新队伍排序为-一-二-),然后再插入第四个……直到最后一个。
#define N 10
#include "stdio.h"
main()
{
int i,j,temp,a[N];
for(i = 0;i < N;i++)
{
scanf("%d",&a[i]);
}
for(i = 1;i < N;i++)
{
temp = a[i];
for(j = i-1;a[j] > temp&&j >= 0;j--)
{
a[j+1] = a[j];
}
j++;
a[j] = temp;
}
for(i = 0;i < N;i++)
{
printf("m",a[i]);
}
printf("n");
}
6, 快速排序法
这个好难……弄了好久……不断修改……还是出问题了,后来参考了网上的一个程序……大体思想是,拿一个数作为基准,分别从右到左和从左到右进行扫描(假定从小到大排列),那么比基准小的就移动到基准的位置上,比基准大的,就移到先前操作空出来的右侧的位子上……额,这个说的有点乱,下面是程序,添加了许多辅助语句,直接运行分析已经很直观了!!就不多写了,免得自己也纠结了。
#include "stdio.h"
//实现从小到大排序
int Partition(int r[],int i,int j)
{
int pivot,m;
pivot = r[i]; //把区间第一个数作为比较的基准
printf("n本次操作区间[=,=]n",i,j);
while(i < j)
{
///
//从区间两端交替向中间扫描,直至i=j为止
while(i < j&&r[j] >= pivot)
{
j--; //如果没有找到比r[i]小的数,那么一直减小j,向左扫描
}
if(i < j)
{
r[i] = r[j];//这里交换找到的第一个比r[i]小的数
i++;
}
///
//输出向左扫描换数操作后的数组当前值
printf("由右向左扫描一次:n");
for(m = 0;m < 8;m++)
{
printf("}",r[m]);
}
printf("��",i,j);
printf("n");
///
while(i < j&&r[i] <= pivot)
{
i++;//如果没有找到比r[i]大的数,那么一直增大i,向右扫描
}
if(i < j)
{
r[j] = r[i]; //这里交换找到的第一个比r[i]大的数
j--;
}
///
//输出向右扫描换数操作后的数组当前值
printf("由左向右扫描一次:n");
for(m = 0;m < 8;m++)
{
printf("}",r[m]);
}
printf("��",i,j);
printf("n");
} //endwhile
r[i] = pivot; //将基准值移动到当前所指数组位,以保证交换后不遗漏
return i;//返回推进后的i值,作为下次分区间的标准
}
void QuickSort(int r[],int low,int high)
{
int pivotpos; //把区间分成两部分,pivotpos是左区间和右区间的分割位
if(low < high)//保证区间存在,也就是区间长度大于1时候才进行排序操作
{
pivotpos = Partition(r,low,high); //这是划分区间并移动数据的操作
QuickSort(r,low,pivotpos - 1);//对左区间递归排序
QuickSort(r,pivotpos + 1,high);//对右区间递归排序
}
}
main()//这一部分没有什么难度,就是数组输出和子函数调用
{
int a[8]={32,13,53,6,45,87,54,23},i;
///
printf("初始数据n");
printf(" a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]n");
for(i = 0;i < 8;i++)
{
printf("}",a[i]);
}
printf(" i j");
printf("n");
printf("n");
///
QuickSort(a,0,7);
///
printf("结果数据n");
for(i = 0;i < 8;i++)
{
printf("}",a[i]);
}
printf("n");
///
}
7, 字符串排序
这个解释起来简单,就是有一堆字符串,按照字母先后次序进行排序。基本思路和冒泡排序法相似。
#include <stdio.h>
#include<string.h>
#define N 5
main()
{
char word[N][30],temp[30];
int i, j;
printf("请输入字符串,假定5个n");
for(i = 0;i < N;i++)
{
scanf("%s",word[i]);
strupr(word[i]); //这里全部大写化,方便排序
}
for(i = 0;i < N;i++)
{
for(j = i+1;j < N;j++)
{
if(strcmp(word[i],word[j]) > 0 ) //比较字符串大小
{
strcpy(temp, word[i]); //如果前者大,则交换
strcpy(word[i], word[j] );
strcpy(word[j], temp);
}
}
}
for( i=0; i<N; i++)
{
printf("n%s", word[i]);
}
printf("n");
}
8, 删除数组中相同的多余项
这个也比较简单,就是数组中保留了多个相同值的项,显然不精简。最终目的只要保留其中一个就可以了,思路是这样的:从第一个开始分别和后面的比较,如果遇到相同的项,把后面所有项往前移一位就可以了。好比一排椅子摆过去,如果不要其中一个的话,只要让椅子上坐的人离开,后面的依次向前整一位。最后把空出来的椅子去掉,数组也一个道理地得到了精简。
其实引申开来,如果要删除某个特定的数,原理也是一样的,需要考虑的是,如果这个特定的数有很多个,如何删除干净?
还有一种操作和删除相反,就是插入,比如一个数组有一堆从小到大排列的数字,要按照这个次序,插入一个数,怎么做?首先从第一个开始,一个个比下去,如果要插入的数比数组中的数大,则继续比。直到它比某个数小了,然后停下步伐,把数组后面的数依次后移一位,腾一个位子给新来的~~
#define N 10
#include <stdio.h>
main()
{
int i,j,k,n,a[N];
n = N;
printf("输入一个数组n");
for(i = 0;i < n;i++)
{
scanf("%d",&a[i]);
}
for(i = 0;i < n-1;i++)
{
for(j = i+1; j < n;j++)
{
if(a[i] == a[j])
{
for(k = j;k < n;k++) //核心语句,把后面前移
{
a[k] = a[k+1];
}
j--;
n--;
}
}
}
printf("n");
for (i = 0;i < n;i++)
{
printf("%d",a[i]);
printf("n");
}
}
9, 九九乘法表
这个表格是很早以前写的,程序大部分语句是花在了排版上,功能很简单,思路也很简单,一行一行输出,i和j其实分别是列和行的乘数因子。
#include "stdio.h"
int main()
{
int i,j,s=0;
printf(" 《《这是一张九九乘法表》》 nn");
printf(" 1 2 3 4 5 6 7 8 9n");
printf(" ___________________________________");
for(i = 1;i < 10;i++)
{
for(j = 1;j < 10;j++)
{
s=i*j;
if(j == 1) printf("n %d | %d ",i,s);
else if(j <= i&&s < 10) printf(" %d ",s);//与下一句功能一样,只为输出美观
else if(j <= i&&s >= 10) printf("%d ",s);
}
}
printf("nn");
}
10, 数组中特定数的查找
其实这个问题很简单,你弄一个够大的i,一个个比较过去就可以。但如果数组很大,同时它又是有序排列的(通常情况都满足这两个条件,如果不满足的话事先排序一下),那么可以用二分法来查找,这样做有个好处:在数组很大的时候,计算量比较小,查询速度快。你想啊,一个西瓜,切一半,然后再一半,再一半……是不是很快就没有了?
#include "stdio.h"
int mid;
int find(int a[],int n,int x)
{
int num1,numlast,find;
num1=0;
numlast=n-1;
find=0;
do{//精髓部分啊!!务必好好看
mid = (num1 + numlast)/2;
if(x == a[mid]) find=1;//直接命中,找到……
else if(x < a[mid]) numlast = mid - 1;//小于中值,那么上限缩小
else if(x > a[mid]) num1 = mid + 1;//大于中值,那么下限增大
}while(num1 <= numlast&&(!find));
return find;
}
11, 进制的转换
网上有个比较经典的算法,思路就是短除法,利用了子函数的递归递归……总的来说方法还是很强大的,支持二到十六进制,还能扩充,只是一般都用二、八、十和十六不是吗?
引出一个问题,给你一个整数,怎么判定它的位数?很简单的一个方法,用它一直除以10,然后记下当得数不为0时的总操作数。
#include "stdio.h"
void convert(int m,int r)
{
char b[17]="0123456789ABCDEF";
if(m!=0)
{
convert(m/r,r);
printf("%c",b[m%r]);
}
}
void main()
{
int m,r;
scanf("%d%d",&m,&r);
convert(m,r);
}
12, 有序数组的合并
这个其实也比较简单,就是有两个数组a和b,他们中的元素都按相同秩序排列,比如都是从小到大,那么现在依然要按照从小到大合并成新的数组c。
#include "stdio.h"
#define N 4
#define M 3
main()
{
int a[N],b[M],c[N+M];
int i,j = 0,k = 0;
printf("请输入数组a的数值n");
for(i = 0;i < N;i++)
{
scanf("%d",&a[i]);
}
printf("请输入数组b的数值n");
for(i = 0;i < M;i++)
{
scanf("%d", &b[i]);
}
i = 0;
printf("重新排序后的数组c为n");
while(i < N&&j < M)
{
if (a[i] < b[j]){c[k++] = a[i++];}
else{c[k++] = b[j++];}
}
while(i < N){c[k++] = a[i++];}
while(j < M){c[k++] = b[j++];}
for(i = 0;i < N+M;i++)
{
printf("%d",c[i]);
printf("n");
}
}
13, 降序数的问题
一道比较经典的题目,却不知道它的实际价值……代码部分现整理如下:
#include "stdio.h"
int drop(int x)
{
int x1 = x,flag = 1;
while(x >= 10&&flag) //判断是否降序数
{
if(x/10 >= x){x /= 10;}
else{flag=0;}
}
return flag;
}
main()
{
int x;
scanf("%d",&x);
if(drop(x)){printf("%d 是降序数n",x);}
else{printf("%d 不是降序数n",x);}
}
14, 求素数
同上,可能是纯数学问题吧……一般也没怎么用到……基本思路就是先把这个数开方,然后依次把它与1到得数这个区间中数相除,看看能否除断。
#include "math.h"
int isprime(int m)
{
int i,k;
k = sqrt(m);
for(i = 2;i < k;i++)
{
if(m%i == 0){return 0;}
}
return 1;
}
15, 求回文
从结构上来说,回文是对称的,比如aabbaa,deed之类的字符串。那么判断一个字符串是不是回文的方法就是:把第一个和最后一个字符比较,把第二个和倒数第二个比较……那么我们可以先创建一个和原字符串逆序的新字符串,然后挨个比较下去,如果有不一样的字符就跳出,完全一样的就能执行循环到最后。
#include <string.h>
#include <stdio.h>
int huiwen(char s[])
{
int i,n = 0;
char ch,s1[100];
strcpy(s1,s);
while(s[n]){n++;}
for(i = 0;i < n/2;i++)
{
ch = s[i];
s[i] = s[n-i-1];
s[n-i-1] = ch;
}
if(strcmp(s1,s) == 0){return 1;}
else{return 0;}
}
16, 求鞍点
其实这个用曲线理解很好玩,你可以想象一个马鞍,所要找的这个店,在x方向上是最高点,但是在y方向上却是最低点!也就是对于一个二维数组,这个数要同时满足两个条件:它是行上面最大的,同时是列上面最小的。
如果要求一个数组中最大或者最小的值,那么一维的就是一个for循环,二维就可以用两个for循环,在每一次比较后都保存下当前的数组位置值,最后循环完毕进行输出就可以了。
#include <stdio.h>
#include <stdlib.h>
main()
{
int a[3][5],x,y,i,j,k;
int max,min;
for(i = 0;i < 3;i++)
{
for(j = 0;j < 5;j++){scanf("%d",&a[i][j]);}
}
for(i = 0;i < 3;i++)
{
max = a[i][0];//横行第一个
y = 0;//横轴因子0
for(k = 1;k < 5;k++)
{
if(max < a[i][k]){y = k;max = a[i][k];}
}//完毕后将会停在本行最大的那个数上!
min = a[i][y];//行上最大的数
x = i;//纵轴因子0
for(k = 0;k < 3;k++)
{
if(min > a[k][y]){x = k;min = a[k][y];}
}//与所有轴的数进行比较,最后得到本列最小的数
if(i == x)//如果纵轴因子和操作行相等,也就是上两个条件同时满足
{
printf("a[%d][%d] = %dn",x,y,a[x][y]);
}
}
}
17, 把数组中每一行的最大值调到主对角线的位置上
这个还是数组的操作,依然用for循环控制……没什么新鲜的地方
#include "stdio.h"
#include "conio.h"
#include "math.h"
void main()
{
int a[][5]={{1,-2,3,-4,5},{-1,2,-3,4,-5},{5,-4,3,-2,1},
{-5,4,-3,2,-1},{1,4,5,3,2}} ;
int i,k,max,sub,temp;
printf("原先的数组n");
for(i = 0;i <= 4;i++)
{
for(k = 0;k <= 4;k++)
{
printf("m",a[i][k]);
}
printf("n");
}
for(i = 0;i < 5;i++)
{
max = fabs(a[i][0]);
sub = 0;
for(k = 1;k <= 4;k++)
{
if(fabs(a[i][k]) > max){max = fabs(a[i][k]);sub = k;}
}//其实这些操作都差不多,就是遇到条件了标记,之后继续循环
temp = a[i][i];
a[i][i] = a[i][sub];
a[i][sub] = temp;//然后进行数字交换,一个循环遍历所有数值,给出最佳满足
}
printf("操作后的数组n");
for(i = 0;i <= 4;i++)
{
for(k = 0;k <= 4;k++)
{
printf("m",a[i][k]);
}
printf("n");
}
}
18, 哥德巴赫猜想的验证?
也就是说:任何大于2的偶数均可表示为两个素数的和。这要用到(14,素数求解中int isprime()这个子函数)。其实怎么说呢,也就是从3开始,一个个试下去,判断差是否也是素数,如果是,则求解成功。如果不是再继续试……越来越发现这写算法大部分都是利用电脑大量计算不要工钱这个特点,进行强迫高强度劳动啊……
#include “stdio.h”
#include “math.h”
//这里要包含例14
void main()
{
int n,x,a,b;
scanf("%d",&x);
for(n = 6;n <= x;n += 2)
{
for(a = 3;a <= n/2;a += 2)
{
if(isprime(a))
{
b = n-a;
if(isprime(b))
{
printf("%d = %d + %dn",n,a,b);
break;//消隐后可显示全部可能!!
}
}
}
}
}
19, 最大公约数和最小公倍数
先求最大公约数,用的是辗转相除法,然后求出最大公约数就可以求最小公倍数了!
#include "stdio.h"
int gys(int m,int n)
//求最大公约数
{
int r;
while(r = m%n){m = n;n = r;}
return(n);
}
int gbs(int m,int n)
//求最小公倍数
{
return(m*n/gys(m,n));
}
20, 求水仙花数
所谓水仙花数就是这样子的:153=13+53+33,也就是它的各位的立方和等于它本身。
下面是一段输出100到999以内所有水仙花数的代码:
#include "stdio.h"
void main()
{
int m,n,i,j,k;
for(m = 100;m <= 999;m++)
{
i = m/100;j = m/10;k = m;
n = i*i*i + j*j*j + k*k*k;
if(m == n)
{
printf("%dn",m);
}
}
}