End;
算法评价:
快速排序的时间主要耗费在划分操作上,对长度为r的区间进行划分,共需r-1次数据的比较。(1)最坏时间复杂度
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数少一个。
因此,快速排序必须做n-1次划分,第i次划分开始区间长度为n-i+1,所需的比较次数为n-i(1<=i<=n-1),故总的比较次数达到最大值Cmax=n(n-1)/2=O(n2);
(2)最好时间复杂度:
在最好情况下,每次划分所取的基准都是当前无序区的“中值”记录,划分的结果是基准的左、右两个无序子区间的长充大致相等。总的关键字比较次数为O(nlgn).
(3)平均时间复杂度:
尽管快速排序的最坏时间为O(n2),但就平均性而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名,它的平均时间复杂度为O(nlogn)。
(4)空间复杂度:
快速排序在系统内部需要一个栈来实现递归,若每次划分较为均匀,需栈空间为O (logn ),最坏情况下,所需的栈空间为O(n)。
六、 堆排序
1、 堆的定义
N 个元素的序列{k1,k2,…,kn},当且仅当满足如下关系时,称之为堆。 Ki<=K2i
或
Ki>=K2i
Ki<=K2i+1
Ki>=K2i+1
(=1,2,…,n div 2)
满足前一种关系称为小根堆,满足后一种关系称为大根堆。
大根堆和小根堆:
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者的堆称为大根堆。如下图所示:
小根椎示例 大根椎示例
注意:堆中任一子树亦是堆
2、 堆的实质在存储
堆实质上是满足如下性质的完全二叉树,可用一维数组连续存储。
(1) 堆对应的完全二叉树中,所有非终端结点的值均不大于(或不小于)其左右儿子结点的值。
(2) 堆顶元素必为序列中n 个元素的最小值(或最大值)
(3) 在堆对应的完全二叉树中,若非终端结点的地址为k ,则它的父亲结的地址应为k/2,它的左
儿子结点的地址为2k ,右儿子结点的地址为2k+1.
3、 堆排序
若在输出堆顶是最小值(或最大值)后,使得剩2余n-1个元素的序列重又建成一个堆,则得到次小值。如此反复执行,便能得到一个有序序列,这个过程称为堆排序。
(1) 堆排序的特点:
堆排序是树型选择排序。其特点是:在排序过程中,将R[1..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。
(2) 堆排序与直接插入排序的区别:
直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保存这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可通过树形结构保存部分比较结果,以减少比较次数。
(3) 实现堆排序需要解决的问题:
一是如何由一个无序序列建成一个初始堆;
10 15 36 25 30 70 70 36 30
15 10
25
二是如何在输出堆顶元素后,调整剩余元素成为一个新的堆。
解决办法:
用“筛选法”调整堆。每趟排序开始前R[1..i]是以R[1]为根的堆,在R[1]与R[i]交换后,新的无序区R[1..i-1]中只有R[1]的值发生了变化。故除R[1]可能违反堆性质外,其余任何结点为根的子树均是堆。因此,当被调整区间是R[low..hign]时,只须调整以R[low]为根的树即可。
具体实现:
R[low]是左、右子树均已是堆,这两棵子树的根R[2low]和R[2low+1]分别是各自子树中数据最小的结点。若R[low]不大于这两个孩子结点的数据,则R[low]未违反堆性质,以R[low]为根的树已是堆,无须调整;否则必须将R[low]和它的两个孩子结点中数据较小者进行交换,即R[low]与R[small](R[small=min(R[2low],R[2low+1])]交换。交换后双可能使结点R[small]违反堆性质。同样由于该结点的两棵子树仍然是堆,故可重复上述的调整过程,对以R[large]为根的树进行调整。此过程直至当前被调整的结点已满足堆性质,或者该结点已是叶子为止。上述过程就像过筛子一样,把较大的关键字逐层筛下去,而将较小的关键字逐层选上来。因此称为“筛选法”。
完整的堆排序程序:
Program heapsort;
Const n=100;
Var a:array[1..100] of integer;
I,x:integer;
Procedure adjust_down(I,m:integer);
Var x:integer;
Begin
While i*2<=m do
Begin
I:=i*2;
If (ia[i]) then inc(i);
If a[i]>a[I div 2] then
Begin
X:=a[I div 2];
A[I div 2]:=a[i];
A[i]:=x;
End
Else break;
End;
End;
Begin
Reandomize;
For i:=1 to n do a[i]:=random(100);
For i:=n div 2 downto 1 do adjust_down(I,n);
For i:=n downto 2 do
Begin
X:=a[i];
A[i]:=a[1];
A[1]:=x;
Adjust_down(1,i-1);
End;
For i:=1 to n do writeln(a[i]);
End.
算法评价:
堆排序的是时间主要由建立初始堆和反复重建堆这两部分的时间开销构成,堆排序的最坏时间复杂度为O(nlogn).堆排序的平均性能较接近于最坏性能。此外,堆排序仅需一个记录大小供交换用的辅助存储空间。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
七、基数排序(多关键字排序)
基数排序是和前面各类排序方法完全不同的一种排序方法。基数排序是借助多关键字排序的思想对单逻辑关键字进行排序的方法。什么是多关键字?每张扑克牌有两个“关键字”花色和面值,将扑克进行排序整理,通常是先按不同的花色分成四堆,再对每地堆按不同的面值调整顺序。
基数排序就是借助多关键字排序的思相,用“分配”和“收集”两种操作对单逻辑关键字进行排序的一种方法。如九个三位数分别是321,214,665,102,874,699,210,333,600。因为关键字是数值,且值都在0<=k<=999范围内,则可把每一个十位数字看成一个关键字,即可认为k由一个关键字(k1,k2,k3)组成,其中k1是百位数,k2是十位数,k3是个位数。
具体排序过程:
第一趟排序,以个位数关键字k3作为关键字分配,个位数是0的有210,600;个位数是1 的有321,……分配完成后,重新收集的结果为210,600,321,102,333,214,874,665,699;第二趟拓序,以十位数关键字k2作为关键字分配,十位数是0的有600,102;十位数是1 的有210,214,……三趟分配收集后的结果即为最终排序结果102,210,214,321,333,600,665,699,874。
基数排序程序:
Progam radixsort;
const n=8;
type link=^node;
node=record
data:integer;
next:link;
end;
var i,j,l,m,k:integer;
s:string;
p,p1:link;
a:array[1..n] of integer;
q,head:array[0..9] of link;
begin
writeln('Enter data:');
for i:=1 to n do read(a[i]);
for i:=5 downto 1 do
begin
for j:=0 to 9 do
begin new(head[j]);head[j]^.next:=nil;q[j]:=head[j] end;
for j:=1 to n do
begin
str(a[j],s);
for k:=1 to 5-length(s) do s:='0'+ s;
m:=ord(s[i])-48;
new(p); p^.data:=a[j]; p^.next:=nil;
q[m]^.next:=p; q[m]:=p;
end;
l:=0;
for j:=0 to 9 do
begin
p:=head[j];
while p^.next<>nil do
begin
l:=l+1;p1:=p;p:=p^.next; dispose(p1);a[l]:=p^.data;
end;
end;
end;
writeln('Sorted data:');
for i:= 1 to n do write(a[i]:6);
end.
八、各种内部排序主法的比较
选择排序主法需要考虑的因素如下:
(1)若n较小(n<=50),则可以采用直接插入乔序或选择排序。由于直接插入排序所需的记录移动操作较选择排序多,因而当记录本身信息量较大时,用直接选择排序较好。
(2)若文件的初始状态已经按关键字基本有序,则选用直接插入排序或冒泡排序为宜。
(3)若n较大,则应采用时间复杂度为O(nlogn)的排序方法,即快速排序、堆排序。快速排序是目前基于比较的排序法中被认为是最好的方法。
作业:
1、军方截获的信息由n(n<=1000)个数字组成,因为是敌国的高端秘密,所以一时不能破获。最原始的
想法就是对这n个数进行k次提问,每次提问只是对第i个数是多少感兴趣,现在要求编程完成k次提问。
输入:第一行n,以下n行是截获的数字,接着一行是k,接着是k行的提问数字
输出:k行依次对应提问的数字对应的输出数字
样例输入:样例输出:
5 7
121 121
1 121
126
121
7
34
2
4
3