C语言经典算法大全.docx

上传人:b****5 文档编号:7690609 上传时间:2023-01-25 格式:DOCX 页数:58 大小:38.64KB
下载 相关 举报
C语言经典算法大全.docx_第1页
第1页 / 共58页
C语言经典算法大全.docx_第2页
第2页 / 共58页
C语言经典算法大全.docx_第3页
第3页 / 共58页
C语言经典算法大全.docx_第4页
第4页 / 共58页
C语言经典算法大全.docx_第5页
第5页 / 共58页
点击查看更多>>
下载资源
资源描述

C语言经典算法大全.docx

《C语言经典算法大全.docx》由会员分享,可在线阅读,更多相关《C语言经典算法大全.docx(58页珍藏版)》请在冰豆网上搜索。

C语言经典算法大全.docx

C语言经典算法大全

C语言经典算法大全

Ø老掉牙

 

Ø数、运算

 

Ø关于赌博

 

Ø集合问题

 

Ø排序

 

Ø搜寻

 

Ø矩阵

 

1.河内之塔

说明河内之塔(TowersofHanoi)是法国人(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学家EdouardLucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒(Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disc),并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损,而也就是世界末日来临之时。

解法如果柱子标为ABC,要由A搬至C,在只有一个盘子时,就将它直接搬至C,当有两个盘子,就将B当作辅助柱。

如果盘数超过2个,将第三个以下的盘子遮起来,就很简单了,每次处理两个盘子,也就是:

A->B、A->C、B->C这三个步骤,而被遮住的部份,其实就是进入程式的递回处理。

事实上,若有n个盘子,则移动完毕所需之次数为2^n-1,所以当盘数为64时,则所需次数为:

264-1=

#include<>

voidhanoi(intn,charA,charB,charC){

if(n==1){

printf("Movesheet%dfrom%cto%c\n",n,A,C);

}

else{

hanoi(n-1,A,C,B);

printf("Movesheet%dfrom%cto%c\n",n,A,C);

hanoi(n-1,B,A,C);

}

}

intmain(){

intn;

printf("请输入盘数:

");

scanf("%d",&n);

hanoi(n,'A','B','C');

return0;

}

说明

Fibonacci为1200年代的欧洲数学家,在他的着作中曾经提到:

「若有一只免子每个月生一只小免子,一个月后小免子也开始生产。

起初只有一只免子,一个月后就有两只免子,二个月后有三只免子,三个月后有五只免子(小免子投入生产)......。

如果不太理解这个例子的话,举个图就知道了,注意新生的小免子需一个月成长期才会投入生产,类似的道理也可以用于植物的生长,这就是Fibonacci数列,一般习惯称之为费氏数列,例如以下:

1、1、2、3、5、8、13、21、34、55、89......

解法

依说明,我们可以将费氏数列定义为以下:

fn=fn-1+fn-2  ifn>1

fn=n      ifn=0,1

#include<>

#include<>

#defineN20

intmain(void){

intFib[N]={0};

inti;

Fib[0]=0;

Fib[1]=1;

for(i=2;i

Fib[i]=Fib[i-1]+Fib[i-2];

for(i=0;i

printf("%d",Fib[i]);

printf("\n");

return0;

}

#include<>

#defineN12

longcombi(intn,intr){

inti;

longp=1;

for(i=1;i<=r;i++)

p=p*(n-i+1)/i;

returnp;

}

voidmain()

{

intn,r,t;

for(n=0;n<=N;n++){

for(r=0;r<=n;r++){

inti;/*排版设定开始*/

if(r==0){

for(i=0;i<=(N-n);i++)

printf("");

}else{

printf("");

}/*排版设定结束*/

printf("%3d",combi(n,r));

}

printf("\n");

}

}

说明

三色旗的问题最早由所提出,他所使用的用语为DutchNationFlag(Dijkstra为荷兰人),而多数的作者则使用Three-ColorFlag来称之。

假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳子上进行这个动作,而且一次只能调换两个旗子。

解法

在一条绳子上移动,在程式中也就意味只能使用一个阵列,而不使用其它的阵列来作辅助,问题的解法很简单,您可以自己想像一下在移动旗子,从绳子开头进行,遇到蓝色往前移,遇到白色留在中间,遇到红色往后移,如下所示:

只是要让移动次数最少的话,就要有些技巧:

如果图中W所在的位置为白色,则W+1,表示未处理的部份移至至白色群组。

如果W部份为蓝色,则B与W的元素对调,而B与W必须各+1,表示两个群组都多了一个元素。

如果W所在的位置是红色,则将W与R交换,但R要减1,表示未处理的部份减1。

注意B、W、R并不是三色旗的个数,它们只是一个移动的指标;什幺时候移动结束呢?

一开始时未处理的R指标会是等于旗子的总数,当R的索引数减至少于W的索引数时,表示接下来的旗子就都是红色了,此时就可以结束移动,如下所示:

#include<>

#include<>

#include<>

#defineBLUE'b'

#defineWHITE'w'

#defineRED'r'

#defineSWAP(x,y){chartemp;\

temp=color[x];\

color[x]=color[y];\

color[y]=temp;}

intmain(){

charcolor[]={'r','w','b','w','w',

'b','r','b','w','r','\0'};

intwFlag=0;

intbFlag=0;

intrFlag=strlen(color)-1;

inti;

for(i=0;i

printf("%c",color[i]);

printf("\n");

while(wFlag<=rFlag){

if(color[wFlag]==WHITE)

wFlag++;

elseif(color[wFlag]==BLUE){

SWAP(bFlag,wFlag);

bFlag++;wFlag++;

}

else{

while(wFlag

rFlag--;

SWAP(rFlag,wFlag);

rFlag--;

}

}

for(i=0;i

printf("%c",color[i]);

printf("\n");

return0;

}

说明老鼠走迷宫是递回求解的基本题型,我们在二维阵列中使用2表示迷宫墙壁,使用1来表示老鼠的行走路径,试以程式求出由入口至出口的路径。

解法老鼠的走法有上、左、下、右四个方向,在每前进一格之后就选一个方向前进,无法前进时退回选择下一个可前进方向,如此在阵列中依序测试四个方向,直到走到出口为止,这是递回的基本题,请直接看程式应就可以理解。

#include<>

#include<>

intvisit(int,int);

intmaze[7][7]={{2,2,2,2,2,2,2},

{2,0,0,0,0,0,2},

{2,0,2,0,2,0,2},

{2,0,0,2,0,2,2},

{2,2,0,2,0,2,2},

{2,0,0,0,0,0,2},

{2,2,2,2,2,2,2}};

intstartI=1,startJ=1;;

}

}

printf("\n");

}

}

voidbacktrack(inti){

intj;

if(i>N){

showAnswer();

}

else{

for(j=1;j<=N;j++){

if(column[j]==1&&

rup[i+j]==1&&lup[i-j+N]==1){

queen[i]=j;

hanoihanoihanoihanoihanoihanoihanoihanoihanoihanoihanoihanoihanoihanoihanoi

ize;s<=LIMIT;s++){

p=s-a[i].size;

newvalue=value[p]+a[i].price;

if(newvalue>value[s]){ize){

printf("%s\t%d\n",

a[item[i]].name,a[item[i]].price);

}

printf("合计\t%d\n",value[LIMIT]);

return0;

}

Java

classFruit{

privateStringname;

privateintsize;

privateintprice;

publicFruit(Stringname,intsize,intprice){

=name;

=size;

=price;

}

publicStringgetName(){

returnname;

}

publicintgetPrice(){

returnprice;

}

publicintgetSize(){

returnsize;

}

}

publicclassKnapsack{

publicstaticvoidmain(String[]args){

finalintMAX=8;

finalintMIN=1;

int[]item=newint[MAX+1];

int[]value=newint[MAX+1];

Fruitfruits[]={

newFruit("李子",4,4500),

newFruit("苹果",5,5700),

newFruit("橘子",2,2250),

newFruit("草莓",1,1100),

newFruit("甜瓜",6,6700)};

for(inti=0;i<;i++){

for(ints=fruits[i].getSize();s<=MAX;s++){

intp=s-fruits[i].getSize();

intnewvalue=value[p]+

fruits[i].getPrice();

if(newvalue>value[s]){etSize()){

"\t"+fruits[item[i]].getPrice());

}

合计\t"+value[MAX]);

}

}

说明蒙地卡罗为摩洛哥王国之首都,该国位于法国与义大利国境,以赌博闻名。

蒙地卡罗的基本原理为以乱数配合面积公式来进行解题,这种以机率来解题的方式带有赌博的意味,虽然在精确度上有所疑虑,但其解题的思考方向却是个值得学习的方式。

解法蒙地卡罗的解法适用于与面积有关的题目,例如求PI值或椭圆面积,这边介绍如何求PI值;假设有一个圆半径为1,所以四分之一圆面积就为PI,而包括此四分之一圆的正方形面积就为1,如下图所示:

如果随意的在正方形中投射飞标(点)好了,则这些飞标(点)有些会落于四分之一圆内,假设所投射的飞标(点)有n点,在圆内的飞标(点)有c点,则依比例来算,就会得到上图中最后的公式。

至于如何判断所产生的点落于圆内,很简单,令乱数产生X与Y两个数值,如果X^2+Y^2等于1就是落在圆内。

#include<>

#include<>

#include<>

#defineN50000

intmain(void){

inti,sum=0;

doublex,y;

srand(time(NULL));

for(i=1;i

x=(double)rand()/RAND_MAX;

y=(double)rand()/RAND_MAX;

if((x*x+y*y)<1)

sum++;

}

printf("PI=%f\n",(double)4*sum/N);

return0;

}

说明除了自身之外,无法被其它整数整除的数称之为质数,要求质数很简单,但如何快速的求出质数则一直是程式设计人员与数学家努力的课题,在这边介绍一个着名的Eratosthenes求质数方法。

解法首先知道这个问题可以使用回圈来求解,将一个指定的数除以所有小于它的数,若可以整除就不是质数,然而如何减少回圈的检查次数?

如何求出小于N的所有质数?

首先假设要检查的数是N好了,则事实上只要检查至N的开根号就可以了,道理很简单,假设A*B=N,如果A大于N的开根号,则事实上在小于A之前的检查就可以先检查到B这个数可以整除N。

不过在程式中使用开根号会精确度的问题,所以可以使用i*i<=N进行检查,且执行更快。

再来假设有一个筛子存放1~N,例如:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21........N

先将2的倍数筛去:

2 3 5 7 9 11 13 15 17 19 21........N

再将3的倍数筛去:

2 3 5 7 11 13 17 19 ........N

再来将5的倍数筛去,再来将7的质数筛去,再来将11的倍数筛去........,如此进行到最后留下的数就都是质数,这就是Eratosthenes筛选方法(EratosthenesSieveMethod)。

检查的次数还可以再减少,事实上,只要检查6n+1与6n+5就可以了,也就是直接跳过2与3的倍数,使得程式中的if的检查动作可以减少。

实作

C

#include<>

#include<>

#defineN1000

intmain(void){

inti,j;

intprime[N+1];

for(i=2;i<=N;i++)

prime[i]=1;

for(i=2;i*i<=N;i++){....]-

[4/239-4/(3*2393)+4/(5*2395)-4/(7*2397)+......]

可以将这个公式整理为:

PI=[16/5-4/239]-[16/(53)-4/(2393)]/3+[16/(55)-4/(2395)]/5+......

也就是说第n项,若为奇数则为正数,为偶数则为负数,而项数表示方式为:

[16/52*n-1-4/2392*n-1]/(2*n-1)

如果我们要计算圆周率至10的负L次方,由于[16/52*n-1-4/2392*n-1]中16/52*n-1比4/2392*n-1来的大,具有决定性,所以表示至少必须计算至第n项:

[16/52*n-1]/(2*n-1)=10-L

将上面的等式取log并经过化简,我们可以求得:

n=L/(2log5)=L/

所以若要求精确度至小数后L位数,则只要求至公式的第n项,其中n等于:

n=[L/]+1

在上式中[]为高斯符号,也就是取至整数(不大于L/的整数);为了计简方便,可以在程式中使用下面这个公式来计简第n项:

[Wn-1/52-Vn-1/(2392)]/(2*n-1)

这个公式的演算法配合大数运算函式的演算法为:

div(w,25,w);

div(v,239,v);

div(v,239,v);

sub(w,v,q);

div(q,2*k-1,q)

至于大数运算的演算法,请参考之前的文章,必须注意的是在输出时,由于是输出阵列中的整数值,如果阵列中整数位数不满四位,则必须补上0,在C语言中只要使用格式指定字%04d,使得不足位数部份自动补上0再输出,至于Java的部份,使用NumberFormat来作格式化。

#include<>

#defineL1000

#defineNL/4+1

s[0]);

for(k=1;k

printf("%04d",s[k]);

printf("\n");

return0;

}

voidadd(int*a,int*b,int*c){

inti,carry=0;

for(i=N+1;i>=0;i--){

c[i]=a[i]+b[i]+carry;

if(c[i]<10000)

carry=0;

else{....,这只要使用除法与余数运算就可以了,例如输入input为abc,则:

a=input/100

b=(input%100)/10

c=input%10

#include<>

#include<>

#include<>

intmain(void){

inta,b,c;

intinput;

printf("寻找Armstrong数:

\n");

for(input=100;input<=999;input++){

a=input/100;

b=(input%100)/10;

c=input%10;

if(a*a*a+b*b*b+c*c*c==input)

printf("%d",input);

}

printf("\n");

return0;

}

说明

现将举行一个餐会,让访客事先填写到达时间与离开时间,为了掌握座位的数目,必须先估计不同时间的最大访客数。

解法

这个题目看似有些复杂,其实相当简单,单就计算访客数这个目的,同时考虑同一访客的来访时间与离开时间,反而会使程式变得复杂;只要将来访时间与离开时间分开处理就可以了,假设访客i的来访时间为x[i],而离开时间为y[i]。

在资料输入完毕之后,将x[i]与y[i]分别进行排序(由小到大),道理很简单,只要先计算某时之前总共来访了多少访客,然后再减去某时之前的离开访客,就可以轻易的解出这个问题。

#include<>

#include<>

#defineMAX100

#defineSWAP(x,y){intt;t=x;x=y;y=t;}

intpartition(int[],int,int);

voidquicksort(int[],int,int);

1953年三月十七日

....

[7]80......

[]......

[]708080......

[70]8080......

[7080]80......

插入排序

像是玩朴克一样,我们将牌分作两堆,每次从后面一堆的牌抽出最前端的牌,然后插入前面一堆牌的适当位置,例如:

排序前:

67

[7792]854367将77插入92前

[677792]4367将67插入77前

[8677792]367将8插入67前

[]67将6插入8前

[92]将84插入92前

[8492]854367将55插入67前

[848592]4367......

[]67......

[92]......

气泡排序法

顾名思义,就是排序时,最大的元素会如同气泡一样移至右端,其利用比较相邻元素的方法,将大的元素交换至右端,所以大的元素会不断的往右移动,直到适当的位置为止。

基本的气泡排序法可以利用旗标的方式稍微减少一些比较的时间,当寻访完阵列后都没有发生任何的交换动作,表示排序已经完成,而无需再进行之后的回圈比较与交换动作,例如:

排序前:

50

[95]95浮出

691850[9095]90浮出

1850[809095]80浮出

50[]......

[95]......

691827[9095]......

6918[809095]由于接下来不会再发生交换动作,排序提早结束

在上面的例子当中,还加入了一个观念,就是当进行至i与i+1时没有交换的动作,表示接下来的i+2至n已经排序完毕,这也增进了气泡排序的效率。

#include<>

#include<>

#include<>

#defineMAX10

#defineSWAP(x,y){intt;t=x;x=y;y=t;}

voidselsort(int[]);3)\n");

}

return0;

}

voidselsort(intnumber[]){

inti,j,k,m;

for(i=0;i

m=i;

for(j=i+1;j

if(number[j]

m=j;

if(i!

=m)

SWAP(number[i],number[m])

printf("第%d次排序:

",i+1);

for(k=0;k

printf("%d",number[k]);

printf("\n");

}

}

voidinsort(intnumber[]){

inti,j,k,tmp;

for(j=1;j

tmp=number[j];

i=j-1;

while(tmp

number[i+1]=number[i];

i--;

if(i==-1)

break;

}

number[i+1]=tmp;

printf("第%d次排序:

",j);

for(k=0;k

printf("%d",number[k]);

printf("\n");

}

}

voidbubsort(intnumber[]){

inti,j,k,flag=1;

for(i=0;i

flag=0;

for(j=0;j

if(number[j+1]

SWAP(number[j+1],number[j]);

flag=1;

}

}

printf("第%d次排序:

",i+1);

for(k=0;k

printf("%d",number[k]);

printf("\n");

}

}

说明

插入排序法由未排序的后半部前端取出一个值,插入已排序前半部的适当位置,概念简单但速度不快。

排序要加快的基本原则之一,是让后一次的排序进行时,尽量利用前一次排序后的结果,以加快排序的速度,Shell排序法即是基于此一概念来改良插入排序法。

解法

Shell排序法最初是于1959所提出,假设要

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1