第六章数组
第6章数组
6.1 问题导引与分析
到目前为止,我们所接触的变量类型(除文件类型外)为简单数据类型,其特点是对单一的变量进行数据处理和操作。在解决实际问题时,常需要处理大量的数据,如对1000个学生的成绩排序、对一批数据进行各种处理等问题,如果用简单变量来存储这些数据,要用到大量的简单变量,无论从存储还是处理都很不现实。Pascal语言引入了数组类型,能方便地解决批量数据处理问题。
数组是程序设计中最常用的结构数据类型,用来描述由固定数目的同一类型的元素组成的数据结构。
从形式上看,Pascal语言数组元素书写格式为:变量名[下标1,下标2,下标3,……],数组的每个元素和下标相关联,根据下标指示数组的元素。
只有一个下标的变量集合称为一维数组。
如:a[1],a[2],a[3],a[4],a[5],……,俗称a数组。
使用数组的方便之处在于数组的下标可以用变量来表示,如上述的a数组元素可以写成a[i]形式,通过灵活控制下标变量i,达到对数组元素进行批量处理和操作的目的。
6.1.1 问题导引
问题6.1 选票统计(Votes)
【问题描述】
国际运动协会组织了一个评选N佳运动员的活动,评选方式很特殊,先由网民投票选举,各国的网民可以任选自己喜爱的运动员,然后从中选出得票高于100万张的运动员N个,对他们用1到N进行编号,重新投票,根据不同的得票值,颁发不同的奖项,现在组织者想知道重新投票后,这N个运动员的票数,请你帮他完成。
【输入格式】
第一行,一个数,表示N个运动员;(1≤N≤1000)
第二行开始为一组以空格隔开的选票,选票值为1到N间的数,以-1为数据结束标志。选票数量不超出长型范围。
【输出格式】
按编号顺序输出编号和票数。
【输入样例】
3
3 1 2 3 2 1 2 3 1 2 2 1 3 3 1 2 3 3 -1
【输出样例】
1 5
2 6
3 7
问题6.2 质数(prime.pas)
【问题描述】
求1到N间的质数。
【输入格式】
一个数N。(1 【输出格式】 以空格隔开的质数。 【输入样例】 10 【输出样例】 2 3 5 7 问题6.3 查找(Find) 【问题描述】 中考成绩出来了,许多考生想知道自己成绩排名情况,于是考试委员会找到了你,让你帮助完成一个成绩查找程序,考生只要输入成绩,即可知道其排名及同名次的人有多少。 【输入格式】 第一行一个数N; 第二行一个数K; 第三行开始N个以空格隔开的从大到小排列的所有学生中考成绩。接着K个待查找的考生成绩。 【输出格式】 K行,每行为一个待查找的考生的名次、同名次的人数、比考生高分的人数。查找不到输出“fail!”。 【输入样例】 10 2 580 570 565 564 564 534 534 534 520 520 564 520 【输出样例】 4 2 3 6 2 8 6.1.2 问题分析 分析问题6.1 对于选票统计问题,如果用人工来统计会是如何统计呢?通常的一种方法是,先写上每个人选的标识符,如:本题中用编号标识,然后一边唱票,一边在对应人的标识符 的下方打上增加票数的标志,如:用“正”字,每增加一票,增加“正”字的一个笔划,当唱票完毕,统计一下每个人选得到的“正”字的笔划数即为每个人选的票数。然而,本题中,N值可以达到1000,且每人的得票值很大,人工完成统计是不可能的,但人工的统计方法是可以借鉴的,将人工的统计方法转换为算法,描述如下。 算法描述: 1.开辟每个人选的票数值存放空间; 2.读入参选人数N值; 3.读入第一张投票号X; 4.当X<>-1时,即读票未结束,反复执行下列操作: (1)对应X号人选的票数加1; (2)读入下一张投票号X; 5.当读完投票,输出参选人的票数。 分析问题6.2 关于质数判断问题,在第四章的例4.4中做过讨论。其算法思想基于数学对质数的定义,因此,在例4.4基础上,我们比较容易得到求1~N之间的质数的一种算法,描述如下。 1.读入N; 2.输出质数2; 3.对3~N间的每个数进行质数判定,为质数的输出。 该算法的时间复杂度为O(n2),当N比较大时,需要一定时间才能出解。 变换角度分析,2是质数,2的倍数一定不是质数,这些数就无需进行质数判定,同理,3是质数,3的倍数一定不是质数,……。于是,先把N个自然数按次序排列起来,1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数。 这种按顺序输出质数的同时,将质数的倍数筛去的算法,称为筛法。用筛法求质数是一种比较快的算法,算法时间复杂度为O(n2)。 这里用到一个名词:算法复杂度。算法复杂度包括时间复杂度和空间、复杂度,时间复杂度指执行算法所需的时间(执行多少次赋值、比较等操作),空间复杂度指执行算法需要消耗多少存储空间。算法的时间和空间复杂度是算法设计和选择一个很重要的考量参数。 分析问题6.3 由于给出的原始数据是有序的,在读入数据过程中可以得到的有效信息有: 1.当读入数与前一个数不同时,名次增1; 2.当读入数与前一个数相同时,相同分数人数增1,累计到当前的人数增1。 因此,为了方便后面的查找,先进行预处理,扫描数据,按从大到小的顺序记下每个不同数据的名次、相同人数、累计人数。在经过预处理后的有序数据中实现查找并输出。 最简单的查找方式为,按顺序查找,找到相同的成绩或找不到后输出。这种查找方式称之为顺序查找,显然,在数据量比较大的情况下,顺序查找的效率是比较低的。 寻求提高效率算法的突破口之一,充分利用问题给予的条件,本问题中给出了一个非常重要的条件就是,数据是有序的。这里引入如何在有序数中快速查找数据的算法----二分查找法。 二分查找算法思想:将N个元素分别分成个数大致相同的两半,取中间数a[n/2],如果x=a[n/2],则找到x,算法终止,如果xa[n/2],则在a[n]的右半部分继续查找,接下去查找的数据范围为上一次数据范围的一半,继续执行此算法。 例:在有序序列[05 13 19 21 37 56 64 75 80 88 92] 中查找数据21和85。 [05 13 19 21 37 56 64 75 80 88 92] [05 13 21 37] 56 64 75 80 88 92 05 13 19 [21 37] 56 64 75 80 88 92 查找K=21的过程(查找成功) [05 13 19 21 37 64 75 80 88 92] 05 13 19 21 37 56 [64 75 80 88 92] 05 13 19 21 37 56 64 75 80 [88 92] 05 13 19 21 37 56 64 75 80 [88 92] 查找K=85的过程(查找失败) 二分查找算法的时间复杂度为O(Log2(N))。 6.1.3 解决方案 为实现问题6.1、问题6.2和问题6.3的算法,需要先解决以下几个问题: 1.如何定义和存储批量数据; 2.如何根据需要直接找到批量数据中的某一个数据。 通过对数组的学习和灵活使用,将有效解决批量数据处理等问题。 6.2 一维数组 6.2.1 一维数组的定义 当数组中每一个元素只带有一个下标时,称为一维数组。 PASCAL语言中,定义数组可以采用以下几种方式。 1.在说明部分的TYPE区中定义数组类型,然后再在VAR区中说明数组,形式如下: TYPE 数组类型标识符=array [下标类型] of 元素类型; 下标类型必须是一个顺序类型,其作用是指定数组下标的编制方式和下标取值范围。 基类型规定了数组元素的类型和性质,可以是integer,char,real,boolean等。 例如: TYPE sample=array [1..10]of integer;{有10个元素的一维数组} VAR a,b: sample; 在说明部分的TYPE区中,由用户定义了一个名为sample的数组类型,在VAR区部分说明了a和b为sample类型的数组。 2.直接在VAR区中定义数组 VAR 数组名:array [下标类型] of 元素类型; 例如: VAR a,b: array [1..10]of integer; 3.数组的常量定义 CONST 数组名:array [下标类型] of 元素类型=(逗号隔开的数据); 逗号隔开的数据个数要与下标类型定义数组元素个数相一致。数据类型和元素类型相一致. (1)const a:array[1..3] of integer=(3,4,5); (2)const Hexadecimal : array[0..15] of char = (‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’); 或者: Hexadecimal : array[0..15] of char = ‘0123456789ABCDEF’; 当在说明部分定义了一个数组之后,PASCAL编译程序为所定义的数组在内存空间开辟一串连续的存储单元。 例如: TYPE color=(red,yellow,blue,while,black); rowtype=array[1..8] of real; coltype=array[‘A’..’E’] of integer; colortype=array[color] of CHAR; VAR a: rowtype; b: coltype; c: colortype; 以上程序段定义了a、b、c三个数组,它们在内存中的排列如下图示意。 a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a数组的存储结构 b[‘A’] b[‘B’] b[‘C’] b[‘D’] b[‘E’] b数组的存储结构 c[red] c[yellow] c[blue] c[while] c[black] c数组的存储结构 6.2.2一维数组的引用 当定义了一个数组,数组中的各个元素就共享一个数组名(即为数组变量名),元素之间通过不同的下标进行区别。对数组的操作归根到底就是对数组元素的操作。 一维数组元素的引用格式为: 数组名[下标表达式] (1)下标表达式值的类型,必须与数组类型定义中的下标类型完全一致,并且不允许超越所定义的下标上界和下界。 (2)数组是一个整体,数组名是一个整体的标识,对数组进行操作,即对其元素操作。数组元素可以像同类型的普通变量那样使用。 例如: a[3]:=34; 对数组a中下标值为3的下标变量赋以34的值。 read(a[4]); 读入一个数存储到下标变量a[4]元素中。 (3)特殊地,如果两个数组类型一致,它们之间可以整个数组元素进行赋值。 例如: VAR a,b,c:array[1..100] of integer; begin …… c:=a;a:=b;b:=c; …… end. 在上面程序中,a,b,c三个数组类型完全一致,c:=a;a:=b;b:=c实现了a,b两个数组所有元素的交换。 例6.1 按顺序从键盘输入50个整数,将这50个数逆序输出,并求位于读入顺序为偶数位上的整数之和。 问题分析: 本题所做的事就是能够按需求自由地输出和处理输入的数据,由于数据元素个数比较多,为了方便灵活使用数据,设计一个数组存放输入的数据,每个数据按读入顺序保存在对应数组元素的下标变量中,这样,将这50个数逆序输出问题就转化为控制数组下标值从50变化到1的问题,求位于读入顺序为偶数位上的整数之和就转化为求数组元素下标值为偶数的问题。 算法描述: 1.定义一个下标范围为[1..50]的数组; 2.按顺序读入数据,存放在相应的数组下标变量中; 3.利用循环语句控制数组下标值从50变化到1,输出数组元素; 4.利用循环语句控制数组下标值从1变化到50,累加下标值为偶数的元素和; 5.输出累加和。 程序设计: program exam6_1; var a:array [1..50] of integer; i:integer; s:longint; begin for i:=1 to 50 do read(a[i]); //读入数据存放在数组中 for i:=50 downto 1 do write(a[i],' '); //逆序输出数组元素值 writeln; s:=0; for i:=1 to 50 do if I mod 2=0 then s:=s+a[I]; //求数组偶数位上的元素值和 writeln('s=',s); end. 针对具体的问题,可以给数组下标变量赋予定的物理意义,方便算法的设计和实现。如:例6.1中,数组下标变量表示第几位上的数组元素,利用循环变量有序地控制下标变量的变化,达到自如选择数组元素解决问题的目的。 6.2.3一维数组的应用 例6.2实现问题6.1算法程序。 设a数组用于存放每个运动员的票数,由于参选运动员最多1000,因此,a数组元素个数设计为1000个。 赋予数组下标变量物理意义为动员编号标识符。 即a[x]表示编号为X运动员的票数。 当读入数X,则X运动员票数加1,表示为a[x]:=a[x]+1,程序实现如下。 Program votes; var a:array[1..1000] of longint; n,x:longint; begin assign(input,'votes.in'); reset(input); assign(output,'votes.out'); rewrite(output); fillchar(a,sizeof(a),0); //表示将数组a的所有元素数赋为0 readln(n); {读入参选人数} read(x); {读入第一张票} while x<>-1 do begin inc(a[x]); {编号为X的运动员票数加1} read(x); {读入选票} end; for i:=1 to n do writeln(i, ' ', a[i]); {按编号顺序输出每个参选运动员有编号和选票} close(input); close(output); end. 例6.3实现问题6.2程序设计。 使用筛法思想求质数算法描述如下: 1.定义布尔型数组a,数组元质a[i]表示数i是否为质数,由于FreePascal布尔型数组默认的初值为false,设当i为合数时,a[i]值为true; 2.读入数据范围n; 3.循环控制数组下标值i从2变化到sqrt(n),实现下列操作: 如果当前i的数组元素为质数,从数组中筛去该元素的所有倍数的元素,即a[i*j]:=true; 4.取出未被筛去的a数组元素存放入p数组中; 5.输出p数组元素。 程序设计: Program prime; var a:array[0..1000000]of boolean;//质数标志数组 p:array[0..500000]of longint; //用于记录质数 pn,n,i,j:longint; begin assign(input,'prime.in'); reset(input); assign(output,'prime.out'); rewrite(output); readln(n); for i:=2 to trunc(sqrt(n)) do //将sqrt(n)内的质数的倍数筛去 begin if not a[i] then for j:=2 to n div i do a[i*j]:=true; end; for i:=2 to n do //将未被筛去的数存入p数组中 if not a[i] then begin inc(pn); p[pn]:=i; end; for i:=1 to pn-1 do //输出质数 write(p[i], ' '); writeln(p[pn]); close(input); close(output); end. 在上述算法程序实现中,每次筛去当前获取的质数的倍数,可能存在某些数被重复筛的现象。如:N=25时,6数字即被2筛去也被3重复筛。如何做到不重复筛呢?筛数的原则是筛去质数的连续倍数,在扫描i是否为质数过程中,i是连续数,程序实现方式可充分利用i,每次筛去已生成的质数i的倍数,直到i扫描完。 Program prime; var a:array[0..1000000]of boolean; p:array[0..500000]of longint; pn,n,i,j:longint; begin assign(input,'prime.in'); reset(input); assign(output,'prime.out'); rewrite(output); readln(n); for i:=2 to n do begin if not a[i] then //当前数为质数,记录质数 begin inc(pn); p[pn]:=i; end; for j:=1 to pn do //筛去p数组中质数和当前i组成的倍数的数 begin if i*p[j]>n then break; //倍数>n 跳出循环 a[i*p[j]]:=true; //倍数为合数标志 if i mod p[j]=0 then break; //如果i 是某个质数的倍数,则跳出循环end; end; for i:=1 to pn-1 do write(p[i], ' '); writeln(p[pn]); close(input); close(output); end. 例6.4实现问题6.3程序设计。 利用二分查找法实现成绩查找算法描述如下: 1.设数组a用于存放不同的分数值,b用于存放相同分数的人数,s用于存放高于此分数的人数。数组下标表示名次。 2.预处理,从高到低读入分数,统计相同分数的人数和高于本分数的人数,统计不同分人数p; 3.读入需要查找的成绩x,二分查找成绩; 4.设变量l、r、mid表示指针,分别指向左端、右端和中间数据,初值为l:=1; r:=p; mid:=(l + r) div 2; 5.如果r>l反复执行下列操作: (1)如果a[mid]=x,退出循环,执行输出; (2)如果a[mid]>x,说明x值位于mid到r之间,改变左指针为l:=mid+1; (3)如果a[mid] (4)继续二分,让mid:=(l+r) div 2; 6.判断退出循环时,a[mid]的值是否为查找值,是,输出名次、相同分数的人数和高于本分数的人数,不是,输出查找不到该分数。 程序设计: Program Find; var a,b,s:array[0..100000]of longint; n,k,i,l,r,mid,p,x:longint; begin assign(input,'find.in'); reset(input); assign(output,'find.out'); rewrite(output); readln(n); readln(k); for i:=1 to n do begin read(x); if a[p]=x then {读入数与前一个数相同,相同分数人数加1,大等于本分数的人数1 } begin inc(b[p]); inc(s[p]); end else begin //读入数与前一个数相同 inc(p); //名次加1 a[p]:=x; //存储分数 b[p]:=1; //同分人数初值为1 s[p]:=s[p-1]+1; //大等于本分数的人数初值为前一分数统计值加1 end; end; for i:=1 to k do //二分查找 begin read(x); l:=1; r:=p; mid:=(l + r) div 2; //左、右和中间指针赋初值 while r>l do //当右指针值不大于左指针值时退出 begin if a[mid]=x then break //查找到退出 else if a[mid]>x then l:=mid+1{若mid指针指向的值大于查找值,则查找值落在右边值赋域,改变左指针位置} else r:=mid-1; {若mid指针指向的值不大于查找值,则查找值落在左边值赋域,改变右指针位置} mid:=(l+r) div 2; //继续二分 end; if a[mid]<>x then writeln(‘fail!’) else writeln(mid,' ',b[mid],' ',s[mid-1]); end; close(input); close(output); end. 例 6.5 对于任意给定的一组数据,将数据按从小到大或从大到小顺序进行排列称为排序。排序是我们比较熟悉的概念,在现实生活中有各种各样的排序问题,有各种各样实现排序的方法,请你例举出你遇到的排序问题和你采用的解决方法,设计实现排序的不同算法和程序。这是一道开放型的问题,你可以对输入和输出增加限制条件。希望先自行思考实现后再看下面的分析。 问题分析: 在第三章中,我们曾经遇到三个数的排序问题,可以获得通过对数据进行比较实现排序的基本思想和方法。然而,当数据量很大的情况下,排序需要解决的基本问题有:1.如何存储数据; 2.如何设计能够收敛的算法,即使问题的规模不断减小不直至解决; 3.考虑算法时间复杂度。 下面将介绍五种排序算法的分析和实现过程。希望通过本问题的分析和实现,除了掌握数组的灵活应用外,更重要的学会如何从不同的角度分析和解决问题。 以下算法和程序实现默认按从小到大的顺序。 先来看一个有限制条件的排序问题: 输入N个数,第一行是N,之后每行一个数.输出N行,为这些数从小到大排序的结果.每个数都是1,000,000以内的正整数。 该问题有一个很重要的条件,就是需要排序的数为0~1,000,000以内整数。如何用上这个条件呢?设数组下标表示每个数,数组元素内容用于存放对应下标数据的个数,如此,当统计好每个数的个数时,数据自然在数组中形成有序的排列了。 算法: 1、开一个0..1,000,000的数组a; 2、对于每个输入的数,对应的数组下标的值增加1; 3、按数组下标i从小到大的顺序,判断数组元素值,如果不为0,输出a[i]个下标值i。 这种不用比较的排序算法称为计数排序。由于只用一重循环就能实现数据的排序,所以,算法时间复杂度为:O(n)。 程序设计: Program Counting_Sort; Var A:array[0..1000000]of longint;//开一个足够大的数组 I,j,n,m:longint; Begin Readln(n);//读入N For i:=1 to n do Begin Readln(m);//读入数据 a[m]:=a[m]+1;//将数据对应下标的数组值+1 end; for i:=0 to 1000000 do for j:=1 to a[i] do writeln(i);//输出数据 end. 计数排序是一种高效的排序算法。但是它的应用有一定的限制,即具体数据的范围不能超过数组大小承受能力的极限。 现在讨论一般的排序问题: 输入N个数,第一行是N,之后每行一个数,输出N行,为这些数从小到大排 序的结果。 分析1:原始想法 以站队为例,将无序的一个队列,按身高从低到高的顺列。一种比较简单的做法:从队列中找最矮的人组成新的队列,接着从原队列中再找最矮的人接在新队列的后面,每次从原队列中找最矮的人接在新队列的后面,直到原队列空了为止。则新队列就是有序的队列。 算法描述为: 1.设a数组存放原始数据,b数据组存放排序后的数据,t布尔数组标志a数组中对应的元素是否被取走; 2.将所有的元素设为未标志; 3.循环N次,重复下列操作: 遍历一次a数组,找出其中未被标志的元素中最小的一个,对它打上标志,取出,放在另外的数组b中; 4.按顺序输出b数组元素。 这是一种朴素的算法,也是比较容易想到的算法。 问题与改进: 上述算法花费了三个数组来实现排序,造成了空间上的极大浪费。有没有可以直接将结果保存在本身数组内的算法呢? (1) 引入交换思想:既然要将元素从小到大排列,那么每当找到一个逆序对(所谓逆序对指I,j两个位置上元素a[i]和a[j],当i (2)新的问题:如何寻找逆序对呢?因为不同的查找方式和顺序有可能导致算法的效率不同,必须有规律地查找。 引出两个本质相同但寻找逆序对方法不同的排序算法:冒泡排序与选择排序。 分析2:冒泡排序的思想 以n个人站队为例,从第1个开始,依次比较相邻的两个人是否逆序对(高在前矮在后),若逆序就交换这两人,即第1个和第2个比,若逆序,交换两人,接着第2个和第3个比,若逆序,交换两人,接着第3个和第4个比,若逆序,交换两人,……,直到n-1和n比较,经过一轮比较后,则把最高的人排到最后,即将最高的人像冒泡一样逐步冒到相应的位置。原n个人的排序问题,转换为n-1个人的排序问题。第二轮从第1个开始,依次比较相邻的两个人是否逆序对,若逆序就交换这两人,直到n-2和n-1比较。……,如此,进行n-1轮后,队列为有序的队列。 从上述分析中可以看出,每进行一轮的比较之后,n个数的排序规模就转化为了n-1个数的排序规模,体现了算法的收敛性。 算法描述为: 1.设a数组存放数据; 2.对尚未排序的各元素从头到尾依次比较相邻的两个元素是否逆序(与欲排 顺序相反),若逆序就交换这两元素; 3.经过第一轮比较后便可把最大(或最小)的元素排好; 4.用同样的方法把剩下的元素逐个进行比较,如果有n 个元素,那么一共要进行n-1 轮比较,就得到了所需要的排序。 程序实现方法:用两重循环完成算法,外重循环i控制每轮要进行多少次的比较,第一轮比较n-1次,第2轮n-2,……,最后一轮比较1次。内重循环j控制每轮i次比较相邻两个元素是否逆序,若逆序就交换这两元素。 算法时间复杂度为O(N^2)。 程序设计: Program Bubble_Sort; Var A:array[0..1000]of longint;//开一个和数据个数一样多的数组 I,j,n,m:longint; Begin Readln(n);//读入N For i:=1 to n do readln(a[i]);//读入数据 For i:=n-1 downto 1 do //进行n-1轮冒泡 For j:=1 to i do //每轮进行i次的比较 If a[j]>a[j+1] then//相邻元素进行比较,如果大的在前面,则交换 Begin M:=a[j]; A[j]:=a[j+1]; A[j+1]:=m; End; For i:=1 to n do writeln(a[i]);//输出排序结果 End. 分析3:选择排序的思想 以n个人站队为例,第一次,从队列中选择最矮的一个人与站在第一个位置上的人交换位置,则第一位置上的人为最矮,n个人的队列排序问题转换为n-1个人的队列排序问题,第二次,从2~n中队列中选择最矮的一个人与站在第二个位置上的人交换位置,n-1个人的队列排序问题转换为n-2个人的队列排序问题,……,第n-1个和第n个人进行比较、交换。经过n-1次选择、交换后,原队列就形成了有序的队列。 算法描述为: 1.设a数组存放数据; 2.在1~n中选择值最小的元素,与第1位置元素交换,则把最小元素放入第1个位置; 3.在2~n中选择值最小的元素,与第2位置元素交换,……; 4.直到第n-1元素与第n个元素比较排序为止。 程序实现方法:用两重循环完成算法,外重循环i控制当前序列最小值存放的数组位置,内循环j控制从i+1到n序列中选择最小的元素所在位置k。 算法时间复杂度为O(N^2)。 程序设计: PROCEDURE selection_Sort; VAR A:array[0..1000]of longint; //开一个和数据个数一样多的数组 I,J,K,t:LONGINT; BEGIN Readln(n);//读入N For i:=1 to n do readln(a[i]);//读入数据 FOR I:=1 TO N-1 DO //进行n-1次选择 K:=I; //最小元素位置初值 FOR J:=I+1 TO N DO //从i+1到n序列中选择最小元素,记下位置 IF A[k]>A[J] THEN k:=j; t:=A[i]; //将最小元素与第i元素交换 A[i]:=A[k]; A[k]:=t; FOR I:=1 TO N DO WRITE(A[I],' ');//输出排序结果 WRITELN; END. 分析4:插入排序思想: 回忆一下打牌时抓牌的情景:为了方便打牌,抓牌时一般一边抓牌一边按花色和大小插入恰当的位置,当抓完所有的牌时,手中的牌是有序的。这能不能应用到排序呢? 运用类比的思想,当读入一个元素时,在已经排序好的序列中,搜寻它正确的位置,再放入读入的元素。但不该忽略一个重要的问题:在插入这个元素前,应当先将它后面的所有元素后移一位,以保证前面处理过的元素不致被覆盖。 算法描述为: 1.设a数组存放数据; 2.从第2个数开始,取出当前数作为待排序数,逐个与前面的数比较,若小于前面数,则前面数后移,当大等于前面数时,插入当前空出的位置; 3.直到第n个数插入正确位置为止。 程序实现方法:用两重循环完成算法,外重循环i控制待排序的数,从第2个数到第n个数,内重循环j控制寻找插入的位置,j值从i-1开始向前扫描,边扫描边将数据后移,寻找到位置,插入当前值。 算法时间复杂度为O(N^2)。 PROCEDURE Insertion_Sort; VAR A:array[0..1000]of longint; //开一个和数据个数一样多的数组 I,J:LONGINT; Begin Readln(n);//读入N For i:=1 to n do readln(a[i]);//读入数据 For i:=2 to n do Begin A[0]:=a[i];j:=i-1;//用a[0]寄存待插入的数