切换到宽版
  • 65816阅读
  • 87回复

NOIP全集(NOIP95-05试题、答案、解题报告、测试数据) [复制链接]

上一主题 下一主题
离线rockman
只看该作者 70 发表于: 2007-03-15
大恩大德,无以言表
太感谢了
离线jackeyyang
只看该作者 71 发表于: 2007-03-24
非常感谢~~~~
离线陈老师
只看该作者 72 发表于: 2007-05-08
谢谢Q!!!!!
离线haizhong
只看该作者 73 发表于: 2007-05-12
呵呵 强 顶
离线29941223lw
只看该作者 74 发表于: 2007-05-13
太感谢了!!!
离线pbh
只看该作者 75 发表于: 2007-05-23
hahaha
离线pbh
只看该作者 76 发表于: 2007-05-23
在下面的插入排序算法中,为了写程序方便我们可以引入一个哨兵元素L[0],它小于L[1..n]中任一记录。所以,我们设元素的类型ElementType中有一个常量-∞,它比可能出现的任何记录都小。如果常量-∞不好事先确定,就必须在决定L是否向前移动之前检查当前位置是否为1,若当前位置已经为1时就应结束第i遍的处理。另一个办法是在第i遍处理开始时,就将L放入L[0]中,这样也可以保证在适当的时候结束第i遍处理。下面的算法中将对当前位置进行判断。

插入排序算法如下:

procedure Selection_Sort(var L:List);
var
i,j:position;
v:ElementType;
begin
1 for i:=First(L)+1 to Last(L) do
begin
2   v:=L;
3   j:=i;
4   while (j<>First(L))and(L[j-1]< v) do //循环找到插入点
  begin
5     L[j]:=L[j-1]; //移动元素
6     j:=j-1;
  end;
7   L[j]:=v;   //插入元素
end;
end;
下面考虑算法Insertion_Sort的复杂性。对于确定的i,内while循环的次数为O(i),所以整个循环体内执行了∑O(i)=O(∑i),其中i从2到n。即比较次数为O(n2)。如果输入序列是从大到小排列的,那么内while循环次数为i-1次,所以整个循环体执行了∑(i-1)=n(n-1)/2次。由此可知,最坏情况下,Insertion_Sort要比较Ω(n2)次。


如果元素类型是一个很大的纪录,则算法第5行要消耗大量的时间,因此有必要分析移动元素的次数。经过分析可知,平均情况下第5行要执行n(n-1)/4次,分析方法与冒泡排序的分析相同。

如果移动元素要消耗大量的时间,则可以用链表来实现线性表,这样Insertion_Sort可以改写如下(当然前一个算法同样也适用于链表,只不过没下面这个好,但是下面算法这个比较复杂):

注意:在下面的算法中链表L增加了一个哨兵单元,其中的元素为-∞,即线性表L的第一个元素是L^.next^

procedure Selection_Sort_II(var L:PList);
var
i,j,tmp:Position;
begin
1 if L^.next=nil then exit; //如果链表L为空则直接退出
2 i:=L^.next; //i指向L的第一个元素,注意,L有一个哨兵元素,因此L^.next^才是L的第一个元素
3 while i^.next<>nil do
begin
4   tmp:=i^.next; //tmp指向L的下一个位置
5   j:=L;
6   while (j<>i)and(tmp^.data>=j^.next^.data) do //从前向后找到tmp的位置,tmp应该插在j后面
7   j:=j^.next;
8   if j<>i then //j=i说明不需要改变tmp的位置
    begin            
9     i^.next:=tmp^.next; //将tmp从i后面摘除
10     tmp^.next:=j^.next; //在j后面插入tmp
11     j^.next:=tmp;
    end
12   else i:=i^.next; //否则i指向下一个元素
end;
end;
上述改进算法主要是利用链表删除和插入元素方便的特性,对于数组则不适用。

插入排序法是一个原地置换排序法,也是一个稳定排序法。插入法虽然在最坏情况下复杂性为θ(n2),但是对于小规模输入来说,插入排序法是一个快速的原地置换排序法。许多复杂的排序法,在规模较小的情况下,都使用插入排序法来进行排序,比如快速排序和桶排序。
离线pbh
只看该作者 77 发表于: 2007-05-23
上述算法将较大的元素看作较重的气泡,每次最大的元素沉到表尾。其中First(L)和Last(L)分别表示线性表L的第一个元素和最后一个元素的位置,swap(x,y)交换变量x,y的值。上述算法简单地将线性表的位置当作整数用for循环来处理,但实际上线性表可能用链表实现;而且上述算法将线性表元素的值当作其键值进行处理。不过这些并不影响表达该算法的基本思想。今后如果不加说明,所有的算法都用这种简化方式表达。

容易看出该算法总共进行了n(n-1)/2次比较。如果swap过程消耗的时间不多的话,主要时间消耗在比较上,因而时间复杂性为O(n2)。但是如果元素类型是一个很大的纪录,则Swap过程要消耗大量的时间,因此有必要分析swap执行的次数。

显然算法Bubble_Sort在最坏情况下调用n(n-1)/2次Swap过程。我们假设输入序列的分布是等可能的。考虑互逆的两个输入序列L1=k1,k2,..,kn和L2=kn,kn-1,..,k1。我们知道,如果ki>kj,且ki在表中排在kj前面,则在冒泡法排序时必定要将kj换到ki前面,即kj向前浮的过程中一定要穿过一次ki,这个过程要调用一次Swap。对于任意的两个元素ki和kj,不妨设ki>kj,或者在L1中ki排在kj前面,或者L2在中ki排在kj前面,两者必居其一。因此对于任意的两个元素ki和kj,在对L1和L2排序时,总共需要将这两个元素对调一次。n个元素中任取两个元素有Cn2 种取法,因此对于两个互逆序列进行排序,总共要调用Cn2 =n(n-1)/2次Swap,平均每个序列要调用n(n-1)/4次Swap。那么算法Bubble_Sort调用Swap的平均次数为n(n-1)/4。

可以对冒泡算法作一些改进,如果算法第二行的某次内循环没有进行元素交换,则说明排序工作已经完成,可以退出外循环。可以用一个布尔变量来记录内循环是否进行了记录交换,如果没有则终止外循环。

冒泡法的另一个改进版本是双向扫描冒泡法(Bi-Directional Bubble Sort)。设被排序的表中各元素键值序列为:

483 67 888 50 255 406 134 592 657 745 683

对该序列进行3次扫描后会发现,第3此扫描中最后一次交换的一对纪录是L[4]和L[5]:

50 67 255 134 | 406 483 592 657 683 745 888

显然,第3次扫描(i=3)结束后L[5]以后的序列都已经排好序了,所以下一次扫描不必到达Last(L)-i=11-4=7,即第2行的for 循环j不必到达7,只要到达4-1=3就可以了。按照这种思路,可以来回地进行扫描,即先从头扫到尾,再从尾扫到头。这样就得到双向冒泡排序算法:

CODE:

procedure Bi-Directional_Bubble_Sort(var L:List);
var
low,up,t,i:position;
begin
1 low:=First(L);up:=Last(L);
2 while up>low do
begin
3   t:=low;
4   for i:=low to up-1 do
5   if L>L[i+1] then
    begin
6     swap(L,L[i+1]);
7     t:=i;
    end;
8   up:=t;
9   for i:=up downto low+1 do
10   if L< L[i-1] then
    begin
11     swap(L,L[i-1]);
12     t:=i;
    end;
13   low:=t;  
end;
end;
离线glaze3d
只看该作者 78 发表于: 2007-07-16
谢谢lz了
离线pmq20
只看该作者 79 发表于: 2007-08-23
狂顶ing
快速回复
限100 字节
 
上一个 下一个