第一章算法.docx
《第一章算法.docx》由会员分享,可在线阅读,更多相关《第一章算法.docx(36页珍藏版)》请在冰豆网上搜索。
第一章算法
第一章算法
1.1什么是算法
算法是程序设计的精髓,程序设计的实质就是构造解决问题的算法,将其解释为计算机语言。
算法是在有限步骤内求解某一问题所使用的一组定义明确的规则。
通俗点说,就是计算机解题的过程。
在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法。
前者是推理实现的算法,后者是操作实现的算法。
一个算法应该具有以下五个重要的特征:
有穷性:
一个算法必须保证执行有限步之后结束;
确切性:
算法的每一步骤必须有确切的定义;
输入:
一个算法有0个或多个输入,以刻画运算对象的初始情况;
输出:
一个算法有一个或多个输出,以反映对输入数据加工后的结果。
没有输出的算法是毫无意义的;
可行性:
算法原则上能够精确地运行,而且人们用笔和纸做有限次运算后即可完成。
1.2算法的表示方法
算法通常有三种表示方法:
自然语言法、程序流程图法、程序法。
结构化程序设计三种程序结构的流程图(N-S图)如下:
1.顺序结构
2.选择结构
3.循环结构
当型循环直到型循环
例题1:
百钱买百鸡问题:
1.3算法分析
算法的复杂性
算法的复杂性是算法效率的度量,是评价算法优劣的重要依据。
一个算法的复杂性的高低体现在运行该算法所需要的计算机资源的多少上面,所需的资源越多,我们就说该算法的复杂性越高;反之,所需的资源越低,则该算法的复杂性越低。
计算机的资源,最重要的是时间和空间(即存储器)资源。
因而,算法的复杂性有时间复杂性和空间复杂性之分。
不言而喻,对于任意给定的问题,设计出复杂性尽可能低的算法是我们在设计算法时追求的一个重要目标;另一方面,当给定的问题已有多种算法时,选择其中复杂性最低者,是我们在选用算法适应遵循的一个重要准则。
因此,算法的复杂性分析对算法的设计或选用有着重要的指导意义和实用价值。
简言之,在算法学习过程中,我们必须首先学会对算法的分析,以确定或判断算法的优劣。
1.时间复杂性:
例1:
设一程序段如下(为讨论方便,每行前加一行号)
(1)fori:
=1tondo
(2)forj:
=1tondo
(3)x:
=x+1
......
试问在程序运行中各步执行的次数各为多少?
解答:
行号 次数(频度)
(1) n+1
(2) n*(n+1)
(3) n*n
可见,这段程序总的执行次数是:
f(n)=2n2+2n+1。
在这里,n可以表示问题的规模,当n趋向无穷大时,如果f(n)的值很小,则算法优。
作为初学者,我们可以用f(n)的数量级O来粗略地判断算法的时间复杂性,如上例中的时间复杂性可粗略地表示为T(n)=O(n2)。
2.空间复杂性:
例2:
将一一维数组的数据(n个)逆序存放到原数组中,下面是实现该问题的两种算法:
算法1:
fori:
=1tondo
b[i]:
=a[n-i+1];
fori:
=1tondo
a[i]:
=b[i];
算法2:
fori:
=1tondiv2do
begin
t:
=a[i];a[i]:
=a[n-i-1];a[n-i-1]:
=t
end;
算法1的时间复杂度为2n,空间复杂度为2n
算法2的时间复杂度为3*n/2,空间复杂度为n+1
显然算法2比算法1优,这两种算法的空间复杂度可粗略地表示为S(n)=O(n)
信息学比赛中,经常是:
只要不超过内存,尽可能用空间换时间。
2.1递归的概念
1.概念
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).
如:
procedurea;
begin
.
a;
.
end;
这种方式是直接调用.
又如:
procedureb;procedurec;
beginbegin
.
..
c;b;
..
end;end;
这种方式是间接调用.
例1计算n!
可用递归公式如下:
1当n=0时
fac(n)={n*fac(n-1)当n>0时
可编写程序如下:
programfac2;
var
n:
integer;
functionfac(n:
integer):
real;
begin
ifn=0thenfac:
=1elsefac:
=n*fac(n-1)
end;
begin
write('n=');readln(n);
writeln('fac(',n,')=',fac(n):
6:
0);
end.
例2楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法.
设n阶台阶的走法数为f(n)
显然有
1n=1
f(n)={2n=2
f(n-1)+f(n-2)n>2
可编程序如下:
programlouti;
varn:
integer;
functionf(x:
integer):
integer;
begin
ifx=1thenf:
=1else
ifx=2thenf:
=2elsef:
=f(x-1)+f(x-2);
end;
begin
write('n=');read(n);
writeln('f(',n,')=',f(n))
end.
2.2如何设计递归算法
1.确定递归公式
2.确定边界(终了)条件
练习:
用递归的方法完成下列问题
1.求数组中的最大数
2.1+2+3+...+n
3.求n个整数的积
4.求n个整数的平均值
5.求n个自然数的最大公约数与最小公倍数
6.有一对雌雄兔,每两个月就繁殖雌雄各一对兔子.问n个月后共有多少对兔子?
7.已知:
数列1,1,2,4,7,13,24,44,...求数列的第n项.
2.3典型例题
例3梵塔问题
如图:
已知有三根针分别用1,2,3表示,在一号针中从小放n个盘子,现要求把所有的盘子
从1针全部移到3针,移动规则是:
使用2针作为过度针,每次只移动一块盘子,且每根针上
不能出现大盘压小盘.找出移动次数最小的方案.
程序如下:
programfanta;
var
n:
integer;
proceduremove(n,a,b,c:
integer);
begin
ifn=1thenwriteln(a,'--->',c)
elsebegin
move(n-1,a,c,b);
writeln(a,'--->',c);
move(n-1,b,a,c);
end;
end;
begin
write('Entern=');
read(n);
move(n,1,2,3);
end.
例4快速排序
快速排序的思想是:
先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1,处理结束.
程序如下:
programkspv;
constn=7;
type
arr=array[1..n]ofinteger;
var
a:
arr;
i:
integer;
procedurequicksort(varb:
arr;s,t:
integer);
vari,j,x,t1:
integer;
begin
i:
=s;j:
=t;x:
=b[i];
repeat
while(b[j]>=x)and(j>i)doj:
=j-1;
ifj>ithenbegint1:
=b[i];b[i]:
=b[j];b[j]:
=t1;end;
while(b[i]<=x)and(i=i+1;
ifi=b[j];b[j]:
=b[i];b[i]:
=t1;end
untili=j;
b[i]:
=x;
i:
=i+1;j:
=j-1;
ifsifiend;
begin
write('inputdata:
');
fori:
=1tondoread(a[i]);
writeln;
quicksort(a,1,n);
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
练习:
1.计算ackerman函数值:
n+1m=0
ack(m,n)={ack(m-1,1)m<>0,n=0
ack(m-1,ack(m,n-1))m<>0,n<>0
求ack(5,4)
回溯是按照某种条件往前试探搜索,若前进中遭到失败,则回过头来另择通路继续搜索.
3.1回溯的设计
1.用栈保存好前进中的某些状态.
2.制定好约束条件
例1由键盘上输入任意n个符号;输出它的全排列.
programhh;
constn=4;
vari,k:
integer;
x:
array[1..n]ofinteger;
st:
string[n];
t:
string[n];
procedureinput;
vari:
integer;
begin
write('Enterstring=');readln(st);
t:
=st;
end;
functionplace(k:
integer):
boolean;
vari:
integer;
begin
place:
=true;
fori:
=1tok-1do
ifx[i]=x[k]then
beginplace:
=false;breakend;
end;
procedureprint;
vari:
integer;
begin
fori:
=1tondowrite(t[x[i]]);
writeln;
end;
begin
input;
k:
=1;x[k]:
=0;
whilek>0do
begin
x[k]:
=x[k]+1;
while(x[k]<=n)and(notplace(k))dox[k]:
=x[k]+1;
ifx[k]>nthenk:
=k-1
elseifk=nthenprint
elsebegink:
=k+1;x[k]:
=0end
end;
end.
例2.n个皇后问题:
programhh;
constn=8;
vari,j,k:
integer;
x:
array[1..n]ofinteger;
functionplace(k:
integer):
boolean;
vari:
integer;
begin
place:
=true;
fori:
=1tok-1do
if(x[i]=x[k])or(abs(x[i]-x[k])=abs(i-k))then
place:
=false;
end;
procedureprint;
vari:
integer;
begin
fori:
=1tondowrite(x[i]:
4);
writeln;
end;
begin
k:
=1;x[k]:
=0;
whilek>0do
begin
x[k]:
=x[k]+1;
while(x[k]<=n)and(notplace(k))dox[k]:
=x[k]+1;
ifx[k]>nthenk:
=k-1
elseifk=nthenprint
elsebegink:
=k+1;x[k]:
=0end
end;
end.
回溯算法的公式如下:
3.2回溯算法的递归实现
由于回溯算法用一栈数组实现的,用到栈一般可用递归实现。
上述例1的递归方法实现如下:
programhh;
constn=4;
vari,k:
integer;
x:
array[1..n]ofinteger;
st:
string[n];
t:
string[n];
procedureinput;
vari:
integer;
begin
write('Enterstring=');readln(st);
t:
=st;
end;
functionplace(k:
integer):
boolean;
vari:
integer;
begin
place:
=true;
fori:
=1tok-1do
ifx[i]=x[k]then
beginplace:
=false;breakend;
end;
procedureprint;
vari:
integer;
begin
fori:
=1tondowrite(t[x[i]]);
writeln;readln;
end;
proceduretry(k:
integer);
vari:
integer;
begin
ifk=n+1thenbeginprint;exitend;
fori:
=1tondo
begin
x[k]:
=i;
ifplace(k)thentry(k+1)
end
end;
begin
input;
try
(1);
end.
例2:
n皇后问题的递归算法如下:
程序1:
programhh;
constn=8;
vari,j,k:
integer;
x:
array[1..n]ofinteger;
functionplace(k:
integer):
boolean;
vari:
integer;
begin
place:
=true;
fori:
=1tok-1do
if(x[i]=x[k])or(abs(x[i]-x[k])=abs(i-k))then
place:
=false;
end;
procedureprint;
vari:
integer;
begin
fori:
=1tondowrite(x[i]:
4);
writeln;
end;
proceduretry(k:
integer);
vari:
integer;
begin
ifk=n+1thenbeginprint;exitend;
fori:
=1tondo
begin
x[k]:
=i;
ifplace(k)thentry(k+1);
end;
end;
begin
try
(1);
end.
程序2:
说明:
当n=8时有30条对角线分别用了l和r数组控制,
用c数组控制列.当(i,j)点放好皇后后相应的对角线和列都为false.递归程序如下:
programnhh;
constn=8;
vars,i:
integer;
a:
array[1..n]ofbyte;
c:
array[1..n]ofboolean;
l:
array[1-n..n-1]ofboolean;
r:
array[2..2*n]ofboolean;
procedureoutput;
vari:
integer;
begin
fori:
=1tondowrite(a[i]:
4);
inc(s);writeln('total=',s);
end;
proceduretry(i:
integer);
varj:
integer;
begin
forj:
=1tondo
begin
ifc[j]andl[i-j]andr[i+j]then
begin
a[i]:
=j;c[j]:
=false;l[i-j]:
=false;r[i+j]:
=false;
ific[j]:
=true;l[i-j]:
=true;r[i+j]:
=true;
end;
end;
end;
begin
fori:
=1tondoc[i]:
=true;
fori:
=1-nton-1dol[i]:
=true;
fori:
=2to2*ndor[i]:
=true;
s:
=0;try
(1);
writeln;
end.
练习:
1.找出所有从m个元素中选取n(n<=m)元素的组合。
2.设有A,B,C,D,E5人从事j1,j2,j3,j4,j55项工作每人只能从事一项,它们的
效益表如下:
j1
j2
j3
j4
j5
A
13
11
10
4
7
B
13
10
10
8
5
C
5
9
7
7
4
D
15
12
10
11
5
E
10
11
8
8
4
求最佳安排,使效益最高.
3.N个数中找出M个数(从键盘上输入正整数N,M后再输入N个正数),要求从N个数中
找出若干个数,使它们的和为M,把满足条件的数组找出来,并统计组数.
4.地图着色。
如下图12个区域用4种颜色着色要求相邻的区域着不同的颜色
5.将任意一正整数(1如:
4=1+1+1+1
=2+1+1
=2+2
=3+1.
排序就是将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程。
4.1简单排序
1.选择排序
选择排序的基本思想是:
对待排序的记录序列进行n-1遍的处理,第1遍处理是将L[1..n]中最小者与L[1]交换位置,第2遍处理是将L[2..n]中最小者与L[2]交换位置,......,第i遍处理是将L[i..n]中最小者与L[i]交换位置。
这样,经过i遍处理之后,前i个记录的位置就已经按从小到大的顺序排列好了。
例1:
输入序列数据按非减顺序输出.
程序如下:
programxzpx;
constn=7;
vara:
array[1..n]ofinteger;
i,j,k,t:
integer;
begin
write('Enterdate:
');
fori:
=1tondoread(a[i]);
writeln;
fori:
=1ton-1do
begin
k:
=i;
forj:
=i+1tondo
ifa[j]=j;
ifk<>ithen
begint:
=a[i];a[i]:
=a[k];a[k]:
=t;end;
end;
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
2.插入排序
插入排序的基本思想:
经过i-1遍处理后,L[1..i-1]己排好序。
第i遍处理仅将L[i]插入L[1..i-1]的适当位置p,原来p后的元素一一向右移动一个位置,使得L[1..i]又是排好序的序列。
例2:
输入序列数据按非减顺序输出.
程序1:
programcrpx;
constn=7;
vara:
array[1..n]ofinteger;
i,j,k,t:
integer;
begin
write('Enterdate:
');
fori:
=1tondoread(a[i]);
writeln;
fori:
=2tondo
begin
k:
=a[i];j:
=i-1;
while(k0)do
begina[j+1]:
=a[j];j:
=j-1end;
a[j+1]:
=k;
end;
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
3.冒泡排序
冒泡排序又称交换排序其基本思想是:
对待排序的记录的关键字进行两两比较,如发现两个
记录是反序的,则进行交换,直到无反序的记录为止。
例:
输入序列数据按非减顺序输出。
程序1:
programmppx;
constn=7;
vara:
array[1..n]ofinteger;
i,j,k,t:
integer;
begin
write('Enterdate:
');
fori:
=1tondoread(a[i]);
fori:
=1ton-1do
forj:
=ndowntoi+1do
ifa[j-1]begint:
=a[j-1];a[j-1]:
=a[j];a[j]:
=tend;
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
程序2:
programmppx;
constn=7;
vara:
array[1..n]ofinteger;
i,j,k,t:
integer;
bool:
boolean;
begin
write('Enterdate:
');
fori:
=1tondoread(a[i]);
i:
=1;bool:
=true;
while(ibegin
bool:
=false;
forj:
=ndowntoi+1do
ifa[j-1]begint:
=a[j-1];a[j-1]:
=a[j];a[j]:
=t;bool:
=trueend;
i:
=i+1;
end;
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
程序3:
programmppx;
constn=7;
vara:
array[1..n]ofinteger;
i,j,k,t:
integer;
begin
write('Enterdate:
');
fori:
=1tondoread(a[i]);
writeln;
k:
=n;
whilek>0do
begin
j:
=k-1;k:
=0;
fori:
=1tojdo
ifa[i]>a[i+1]then
begint:
=a[i];a[i]:
=a[i+1];a[i+1]:
=t;k:
=i;end;
end;
write('outputdata:
');
fori:
=1tondowrite(a[i]:
6);
writeln;
end.
返回
4.2快速排序
快速排序的思想是:
先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一