导航:首页 > 源码编译 > 各个排序算法适用情况和长处

各个排序算法适用情况和长处

发布时间:2023-06-13 04:52:08

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:输入序列数据按非减顺序输出.

程序如下:

program xzpx;
const n=7;
var a:array[1..n] of integer;
i,j,k,t:integer;
begin
write('Enter date:');
for i:= 1 to n do read(a[i]);
writeln;
for i:=1 to n-1 do
begin
k:=i;
for j:=i+1 to n do
if a[j]<a[k] then k:=j;
if k<>i then
begin t:=a[i];a[i]:=a[k];a[k]:=t;end;
end;
write('output data:');
for i:= 1 to n do write(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:

program crpx;
const n=7;
var a:array[1..n] of integer;
i,j,k,t:integer;
begin
write('Enter date:');
for i:= 1 to n do read(a[i]);
writeln;
for i:=2 to n do
begin
k:=a[i];j:=i-1;
while (k<a[j]) and (j>0) do
begin a[j+1]:=a[j];j:=j-1 end;
a[j+1]:=k;
end;
write('output data:');
for i:= 1 to n do write(a[i]:6);
writeln;
end.

3.冒泡排序

冒泡排序又称交换排序其基本思想是:对待排序的记录的关键字进行两两比较,如发现两个

记录是反序的,则进行交换,直到无反序的记录为止。

例:输入序列数据按非减顺序输出。

程序1:

program mppx;
const n=7;
var a:array[1..n] of integer;
i,j,k,t:integer;
begin
write('Enter date:');
for i:= 1 to n do read(a[i]);
for i:=1 to n -1 do
for j:=n downto i+1 do
if a[j-1]<a[j] then
begin t:=a[j-1];a[j-1]:=a[j];a[j]:=t end;
write('output data:');
for i:= 1 to n do write(a[i]:6);
writeln;
end.

程序2:

program mppx;
const n=7;
var a:array[1..n] of integer;
i,j,k,t:integer;
bool:boolean;
begin
write('Enter date:');
for i:= 1 to n do read(a[i]);
i:=1;bool:=true;
while (i<n) and bool do
begin
bool:=false;
for j:=n downto i+1 do
if a[j-1]<a[j] then
begin t:=a[j-1];a[j-1]:=a[j];a[j]:=t;bool:=true end;
i:=i+1;
end;
write('output data:');
for i:= 1 to n do write(a[i]:6);
writeln;
end.

程序3:

program mppx;
const n=7;
var a:array[1..n] of integer;
i,j,k,t:integer;
begin
write('Enter date:');
for i:= 1 to n do read(a[i]);
writeln;
k:=n;
while k>0 do
begin
j:=k-1;k:=0;
for i:=1 to j do
if a[i]>a[i+1] then
begin t:=a[i];a[i]:=a[i+1];a[i+1]:=t;k:=i;end;
end;
write('output data:');
for i:= 1 to n do write(a[i]:6);
writeln;
end.

返回

4.2快速排序

快速排序的思想是:先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1, 处理结束.

例:输入一组数据小到大排序.

程序1:

program kspv;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i:integer;
procere quicksort(var b:arr; s,t:integer);
var i,j,x,t1:integer;
begin
i:=s;j:=t;x:=b[i];
repeat
while (b[j]>=x) and (j>i) do j:=j-1;
if j>i then begin t1:=b[i]; b[i]:=b[j];b[j]:=t1;end;
while (b[i]<=x) and (i<j) do i:=i+1;
if i<j then begin t1:=b[j];b[j]:=b[i];b[i]:=t1; end
until i=j;
b[i]:=x;
i:=i+1;j:=j-1;
if s<j then quicksort(b,s,j);
if i<t then quicksort(b,i,t);
end;
begin
write('input data:');
for i:=1 to n do read(a[i]);
writeln;
quicksort(a,1,n);
write('output data:');
for i:=1 to n do write(a[i]:6);
writeln;
end.

程序2:

program kspv;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i:integer;
procere quicksort(var b:arr; s,t:integer);
var i,j,x:integer;
begin
i:=s;j:=t;x:=b[i];
repeat
while (b[j]>=x) and (j>i) do j:=j-1;
if j>i then begin b[i]:=b[j];i:=i+1;end;
while (b[i]<=x) and (i<j) do i:=i+1;
if i<j then begin b[j]:=b[i];j:=j-1; end
until i=j;
b[i]:=x;
i:=i+1;j:=j-1;
if s<j then quicksort(b,s,j);
if i<t then quicksort(b,i,t);
end;
begin
write('input data:');
for i:=1 to n do read(a[i]);
writeln;
quicksort(a,1,n);
write('output data:');
for i:=1 to n do write(a[i]:6);
writeln;
end.

返回

4.3希尔排序

基本思想:将整个无序序列分割成若干小的子序列分别进行插入排序或冒泡排序。

序列分割方法:将相隔某个增量h的元素构成一个子序列。在排序过程中,逐次减小这个增量,最后当h减到1时,进行一次插入排序或冒泡排序,排序就完成。增量序列一般采用:d1=n div 2 ,di=di-1 div 2 ;i=2,3,4.....其中n为待排序序列的长度。

程序1:(子序列是插入排序)

program xepx;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i,j,t,d:integer;
bool:boolean;
begin
write('input data:');
for i:=1 to n do read(a[i]);
writeln;
d:=n;
while d>1 do
begin
d:=d div 2;
for j:=d+1 to n do
begin
t:=a[j];i:=j-d;
while (i>0) and (a[i]>t) do
begin a[i+d]:=a[i];i:=i-d;end;
a[i+d]:=t;
end;
end;
write('output data:');
for i:=1 to n do write(a[i]:6);
writeln;
end.

程序2:(子序列是冒泡排序)

program xepx;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i,temp,d:integer;
bool:boolean;
begin
write('input data:');
for i:=1 to n do read(a[i]);
writeln;
d:=n
while d>1 do
begin
d:=d div 2;
repeat
bool:=true;
for i:=1 to n-d do
if a[i]>a[i+d] then
begin temp:=a[i];a[i]:=a[i+d];a[i+d]:=temp; bool:=false end;
until bool;
end;
write('output data:');
for i:=1 to n do write(a[i]:6);
writeln;
end.

返回

4.4 堆排序与二叉树排序

1.堆排序

堆:设有数据元素的集合(R1,R2,R3,...Rn)它们是一棵顺序二叉树的结点且有

Ri<=R2i 和Ri<=R2i+1(或>=)

堆的性质:堆的根结点上的元素是堆中的最小元素,且堆的每一条路径上的元素都是有序的。

堆排序的思想是:

1)建初始堆(将结点[n/2],[ n/2]-1,...3,2,1分别调成堆)

2)当未排序完时

输出堆顶元素,删除堆顶元素,将剩余的元素重新建堆。

程序如下:

program ipx;
const n=8;
type arr=array[1..n] of integer;
var a:arr;i:integer;
procere sift(var a:arr;l,m:integer);
var i,j, t:integer;
begin
i:=l;j:=2*i;t:=a[i];
while j<=m do
begin
if (j<m) and (a[j]>a[j+1]) then j:=j+1;
if t>a[j] then
begin a[i]:=a[j];i:=j;j:=2*i; end
else exit;
a[i]:=t;
end;

end;
begin
for i:=1 to n do read(a[i]);
for i:=(n div 2) downto 1 do
sift(a,i,n);
for i:=n downto 2 do
begin
write(a[1]:4);
a[1]:=a[i];
sift(a,1,i-1);
end;
writeln(a[1]:4);
end.

2.二叉树排序

排序二叉树:每一个参加排列的数据对应二叉树的一个结点,且任一结点如果有左(右)子树,则左(右)子树各结点的数据必须小(大)于该结点的数据。中序遍历排序二叉树即得排序结果。程序如下:

program pxtree;
const
a:array[1..8] of integer=(10,18,3,8,12,2,7,3);
type point=^nod;
nod=record
w:integer;
right,left:point ;
end;
var root,first:point;k:boolean;i:integer;
procere hyt(d:integer;var p:point);
begin
if p=nil then
begin
new(p);
with p^ do begin w:=d;right:=nil;left:=nil end;
if k then begin root:=p; k:=false end;
end
else with p^ do if d>=w then hyt(d,right) else hyt(d,left);
end;
procere hyt1(p:point);
begin
with p^ do
begin
if left<>nil then hyt1(left);
write(w:4);
if right<>nil then hyt1(right);
end
end;
begin
first:=nil;k:=true;
for i:=1 to 8 do hyt(a[i],first);
hyt1(root);writeln;
end.

返回

4.5 归并排序

归并就是将多个有序的数列合成一个有序的数列。将两个有序序列合并为一个有序序列叫二路归并(merge).归并排序就是n长度为1的子序列,两两归并最后变为有序的序列。

1.二路归并

例1:将有序表L1=(1,5,7),L2=(2,3,4,6,8,9,10)合并一个有序表 L.

program gb;

const m=3;n=7;

type arrl1=array[1..m] of integer;

arrl2=array[1..n] of integer;

arrl=array[1..m+n] of integer;

var a:arrl1;

b:arrl2;

c:arrl;

i,j,k,r:integer;

begin

write('Enter l1(sorted):');

for i:=1 to m do read(a[i]);

write('Enter l2(sorted):');

for i:=1 to n do read(b[i]);

i:=1;j:=1;k:=0;

while (i<=m) and (j<=n) do

begin

k:=k+1;

if a[i]<=b[j] then begin c[k]:=a[i];i:=i+1; end

else begin c[k]:=b[j];j:=j+1;end;

end;

if i<=m then for r:=i to m do c[m+r]:=a[r];

if j<=n then for r:=j to n do c[n+r]:=b[r];

writeln('Output data:');

for i:=1 to m+n do write(c[i]:5);

writeln;

end.

2.归并排序

program gbpx;
const maxn=7;
type arr=array[1..maxn] of integer;
var a,b,c:arr;
i:integer;
procere merge(r:arr;l,m,n:integer;var r2:arr);
var i,j,k,p:integer;
begin
i:=l;j:=m+1;k:=l-1;
while (i<=m)and (j<=n) do
begin
k:=k+1;
if r[i]<=r[j] then begin r2[k]:=r[i];i:=i+1 end
else begin r2[k]:=r[j];j:=j+1 end
end;
if i<=m then
for p:=i to m do begin k:=k+1;r2[k]:=r[p] end;
if j<=n then
for p:=j to n do begin k:=k+1;r2[k]:=r[p] end;
end;
procere mergesort( var r,r1:arr;s,t:integer);
var k:integer; c:arr;
begin
if s=t then r1[s]:=r[s] else
begin
k:=(s+t) div 2;
mergesort(r,c,s,k);
mergesort(r,c,k+1,t);
merge(c,s,k,t,r1)
end;
end;
begin
write('Enter data:');
for i:= 1 to maxn do
read(a[i]);
mergesort(a,b,1,maxn);
for i:=1 to maxn do
write(b[i]:9);
writeln;
end.

返回

4.6 线形排序

以上我们讨论的算法均是基于比较的排序算法,在排序算法中有基于数字本身的算法:计数、桶、基数排序。

1.计数排序

基本思想是对于序列中的每一元素x,确定序列中小于x的元素的个数。

例:n个整数序列且每个值在[1,m],排序之。

program jspx;
const m=6;n=8;
var i,j:integer;
a,b:array[1..n] of integer;
c:array[1..m] of integer;
begin
writeln('Enter data:');
for i:=1 to n do read(a[i]);
for i:=1 to m do c[i]:=0;
for i:=1 to n do c[a[i]]:=c[a[i]]+1;
for i:=2 to m do c[i]:=c[i]+c[i-1];
for i:=n downto 1 do
begin
b[c[a[i]]]:=a[i];
c[a[i]]:=c[a[i]]-1;
end;
writeln('Sorted data:');
for i:= 1 to n do
write(b[i]:6);
end.

2.桶排序

桶排序的思想是若待排序的记录的关键字在一个明显有限范围内(整型)时,可设计有限个有序桶,每个桶装入一个值,顺序输出各桶的值,将得到有序的序列。

例:输入n个0到100之间的整数,由小到大排序输出。

program tpx;
const n=7;
var b:array[0..100] of integer;
k:0..100;
i:integer;
begin
write('Enter date:(0-100)');
for i:=0 to 100 do b[i]:=0;
for i:= 1 to n do
begin
read(k);
b[k]:=b[k]+1;
end;
writeln('Output data:');
for i:=0 to 100 do
while b[i]>0 do begin write(i:6);b[i]:=b[i]-1 end;
writeln;
end.

3.基数排序

基本思想是对n个元素依次按k,k-1,...1位上的数字进行桶排序。

program jspx;
const n=8;
type link=^node;
node=record
data:integer;
next:link;
end;
var i,j,l,m,k:integer;
a:array[1..n] of integer;
s:string;
q,head:array[0..9] of link;
p,p1: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.

4.7各种排序算法的比较

1.稳定性比较

插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的

选择排序、希尔排序、快速排序、堆排序是不稳定的

2.时间复杂性比较

插入排序、冒泡排序、选择排序的时间复杂性为O(n2)

其它非线形排序的时间复杂性为O(nlog2n)

线形排序的时间复杂性为O(n);

3.辅助空间的比较

线形排序、二路归并排序的辅助空间为O(n),其它排序的辅助空间为O(1);

4.其它比较

插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。

反而在这种情况下,快速排序反而慢了。

当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

当n较大时,关键字符素比较随机,对稳定性没要求宜用快速排序。

当n较大时,关键字符素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下。

宜用归并排序。

当n较大时,关键字符素可能出现本身是有序的,对稳定性没有要求时宜用堆排序。

2. 排序算法概述

十大排序算法:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序、希尔排序、计数排序,基数排序,桶排序

稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定 :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,前一个键排序的结果可以为后一个键排序所用。

算法的复杂度往往取决于数据的规模大小和数据本身分布性质。
时间复杂度 : 一个算法执行所耗费的时间。
空间复杂度 :对一个算法在运行过程中临时占用存储空间大小的量度。
常见复杂度由小到大 :O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)

在各种不同算法中,若算法中语句执行次数(占用空间)为一个常数,则复杂度为O(1);
当一个算法的复杂度与以2为底的n的对数成正比时,可表示为O(log n);
当一个算法的复杂度与n成线性比例关系时,可表示为O (n),依次类推。

冒泡、选择、插入排序需要两个for循环,每次只关注一个元素,平均时间复杂度为
(一遍找元素O(n),一遍找位置O(n))
快速、归并、堆基于分治思想,log以2为底,平均时间复杂度往往和O(nlogn)(一遍找元素O(n),一遍找位置O(logn))相关
而希尔排序依赖于所取增量序列的性质,但是到目前为止还没有一个最好的增量序列 。例如希尔增量序列时间复杂度为O(n²),而Hibbard增量序列的希尔排序的时间复杂度为 , 有人在大量的实验后得出结论;当n在某个特定的范围后希尔排序的最小时间复杂度大约为n^1.3。

从平均时间来看,快速排序是效率最高的:
快速排序中平均时间复杂度O(nlog n),这个公式中隐含的常数因子很小,比归并排序的O(nlog n)中的要小很多,所以大多数情况下,快速排序总是优于合并排序的。

而堆排序的平均时间复杂度也是O(nlog n),但是堆排序存在着重建堆的过程,它把根节点移除后,把最后的叶子结点拿上来后需要重建堆,但是,拿上的值是要比它的两个叶子结点要差很多的,一般要比较很多次,才能回到合适的位置。堆排序就会有很多的时间耗在堆调整上。

虽然快速排序的最坏情况为排序规模(n)的平方关系,但是这种最坏情况取决于每次选择的基准, 对于这种情况,已经提出了很多优化的方法,比如三取样划分和Dual-Pivot快排。
同时,当排序规模较小时,划分的平衡性容易被打破,而且频繁的方法调用超过了O(nlog n)为
省出的时间,所以一般排序规模较小时,会改用插入排序或者其他排序算法。

一种简单的排序算法。它反复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。这个工作重复地进行直到没有元素再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为元素会经由交换慢慢“浮”到数列的顶端。
1.从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个;
2.对每一对相邻元素作同样的工作,从开始第一对到尾部的最后一对,这样在最后的元素应该会是最大(小)的数;
3.重复步骤1~2,重复次数等于数组的长度,直到排序完成。

首先,找到数组中最大(小)的那个元素;
其次,将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就和自己交换);
再次,在剩下的元素中找到最大(小)的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。
这种方法叫做选择排序,因为它在不断地选择剩余元素之中的最大(小)者。

对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
为了给要插入的元素腾出空间,我们需要将插入位置之后的已排序元素在都向后移动一位。
插入排序所需的时间取决于输入中元素的初始顺序。例如,对一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比对随机顺序的数组或是逆序数组进行排序要快得多。
总的来说,插入排序对于部分有序的数组十分高效,也很适合小规模数组。

一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1 次移动。
希尔排序为了加快速度简单地改进了插入排序,也称为缩小增量排序,同时该算法是突破O(n^2)的第一批算法之一。
希尔排序是把待排序数组按一定数量的分组,对每组使用直接插入排序算法排序;然后缩小数量继续分组排序,随着数量逐渐减少,每组包含的元素越来越多,当数量减至 1 时,整个数组恰被分成一组,排序便完成了。这个不断缩小的数量,就构成了一个增量序列。

在先前较大的增量下每个子序列的规模都不大,用直接插入排序效率都较高,尽管在随后的增量递减分组中子序列越来越大,由于整个序列的有序性也越来越明显,则排序效率依然较高。
从理论上说,只要一个数组是递减的,并且最后一个值是1,都可以作为增量序列使用。有没有一个步长序列,使得排序过程中所需的比较和移动次数相对较少,并且无论待排序列记录数有多少,算法的时间复杂度都能渐近最佳呢?但是目前从数学上来说,无法证明某个序列是“最好的”。
常用的增量序列
希尔增量序列 :{N/2, (N / 2)/2, ..., 1},其中N为原始数组的长度,这是最常用的序列,但却不是最好的
Hibbard序列:{2^k-1, ..., 3,1}
Sedgewick序列:{... , 109 , 41 , 19 , 5,1} 表达式为

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。
对于给定的一组数据,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序后,再用递归方法将排好序的半子表合并成为越来越大的有序序列。
为了提升性能,有时我们在半子表的个数小于某个数(比如15)的情况下,对半子表的排序采用其他排序算法,比如插入排序。
若将两个有序表合并成一个有序表,称为2-路归并,与之对应的还有多路归并。

快速排序(Quicksort)是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。
首先任意选取一个数据(比如数组的第一个数)作为关键数据,我们称为基准数(Pivot),然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序,也称为分区(partition)操作。
通过一趟快速排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数组变成有序序列。
为了提升性能,有时我们在分割后独立的两部分的个数小于某个数(比如15)的情况下,会采用其他排序算法,比如插入排序。

基准的选取:最优的情况是基准值刚好取在无序区数值的中位数,这样能够最大效率地让两边排序,同时最大地减少递归划分的次数,但是一般很难做到最优。基准的选取一般有三种方式,选取数组的第一个元素,选取数组的最后一个元素,以及选取第一个、最后一个以及中间的元素的中位数(如4 5 6 7, 第一个4, 最后一个7, 中间的为5, 这三个数的中位数为5, 所以选择5作为基准)。
Dual-Pivot快排:双基准快速排序算法,其实就是用两个基准数, 把整个数组分成三份来进行快速排序,在这种新的算法下面,比经典快排从实验来看节省了10%的时间。

许多应用程序都需要处理有序的元素,但不一定要求他们全部有序,或者不一定要一次就将他们排序,很多时候,我们每次只需要操作数据中的最大元素(最小元素),那么有一种基于二叉堆的数据结构可以提供支持。
所谓二叉堆,是一个完全二叉树的结构,同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。在一个二叉堆中,根节点总是最大(或者最小)节点。
堆排序算法就是抓住了这一特点,每次都取堆顶的元素,然后将剩余的元素重新调整为最大(最小)堆,依次类推,最终得到排序的序列。

推论1:对于位置为K的结点 左子结点=2 k+1 右子结点=2 (k+1)
验证:C:2 2 2+1=5 2 (2+1)=6
推论2:最后一个非叶节点的位置为 (N/2)-1,N为数组长度。
验证:数组长度为6,(6/2)-1=2

计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序,而且待排序元素值分布较连续、跨度小的情况。
计数排序是一个排序时不比较元素大小的排序算法。
如果一个数组里所有元素都是整数,而且都在0-K以内。对于数组里每个元素来说,如果能知道数组里有多少项小于或等于该元素,就能准确地给出该元素在排序后的数组的位置。

桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,利用某种函数的映射关系将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序)。
桶排序利用函数的映射关系,减少了几乎所有的比较工作。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对桶中的少量数据做排序即可。

常见的数据元素一般是由若干位组成的,比如字符串由若干字符组成,整数由若干位0~9数字组成。基数排序按照从右往左的顺序,依次将每一位都当做一次关键字,然后按照该关键字对数组排序,同时每一轮排序都基于上轮排序后的结果;当我们将所有的位排序后,整个数组就达到有序状态。基数排序不是基于比较的算法。
基数是什么意思?对于十进制整数,每一位都只可能是0~9中的某一个,总共10种可能。那10就是它的基,同理二进制数字的基为2;对于字符串,如果它使用的是8位的扩展ASCII字符集,那么它的基就是256。

基数排序 vs 计数排序 vs 桶排序

基数排序有两种方法:
MSD 从高位开始进行排序
LSD 从低位开始进行排序
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
基数排序:根据键值的每位数字来分配桶
计数排序:每个桶只存储单一键值
桶排序:每个桶存储一定范围的数值

有时,待排序的文件很大,计算机内存不能容纳整个文件,这时候对文件就不能使用内部排序了(我们一般的排序都是在内存中做的,所以称之为内部排序,而外部排序是指待排序的内容不能在内存中一下子完成,它需要做内外存的内容交换),外部排序常采用的排序方法也是归并排序,这种归并方法由两个不同的阶段组成:
采用适当的内部排序方法对输入文件的每个片段进行排序,将排好序的片段(成为归并段)写到外部存储器中(通常由一个可用的磁盘作为临时缓冲区),这样临时缓冲区中的每个归并段的内容是有序的。
利用归并算法,归并第一阶段生成的归并段,直到只剩下一个归并段为止。

例如要对外存中4500个记录进行归并,而内存大小只能容纳750个记录,在第一阶段,我们可以每次读取750个记录进行排序,这样可以分六次读取,进行排序,可以得到六个有序的归并段
每个归并段的大小是750个记录,并将这些归并段全部写到临时缓冲区(由一个可用的磁盘充当)内了,这是第一步的排序结果。
完成第二步该怎么做呢?这时候归并算法就有用处了。

3. 常见排序算法归纳

排序算法一般分类:

比较两个相邻的元素,将值大的元素交换至右端。

依次比较两个相邻的数,将小数放到前面,大数放到后面

即在第一趟:首先比较第1个数和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此一直继续下去,直到比较最后两个数,将小数放前,大数放后。然后重复第一趟步骤,直到所有排序完成。

第一趟比较完成后,最后一个数一定是数组中最大的一个数,所以第二趟比较的时候最后一个数不参与比较。

第二趟完成后,倒数第二个数也一定是数组中第二大的数,所以第三趟比较的时候最后两个数不参与比较。

依次类推......

输出结果:

冒泡排序的优点: 每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。

用时间复杂度来说:

从一个数组中随机选出一个数N,通过一趟排序将数组分割成三个部分,1、小于N的区域 2、等于N的区域 3、大于N的区域,然后再按照此方法对小于区的和大于区分别递归进行,从而达到整个数据变成有序数组。

如下图:

假设最开始的基准数据为数组的第一个元素23,则首先用一个临时变量去存储基准数据,即 tmp=23 ,然后分别从数组的两端扫描数组,设两个指示标志: low 指向起始位置, high 指向末尾。

首先从后半部分开始,如果 扫描到的值大于基准数据 就让 high-1 ,如果发现有元素比该基准数据的值小,比如上面的 18 <= tmp ,就让 high位置的值赋值给low位置 ,结果如下:

然后开始从前往后扫描,如果扫描到的值小于基准数据就让 low+1 ,如果发现有元素大于基准数据的值,比如上图 46 >= tmp ,就再将 low 位置的值赋值给 high 位置的值,指针移动并且数据交换后的结果如下:

然后再开始从前往后遍历,直到 low=high 结束循环,此时low或者high的下标就是 基准数据23在该数组中的正确索引位置 ,如下图所示:

这样一遍遍的走下来,可以很清楚的知道,快排的本质就是把比基准数据小的都放到基准数的左边,比基准数大的数都放到基准数的右边,这样就找到了该数据在数组中的正确位置。

然后采用递归的方式分别对前半部分和后半部分排序,最终结果就是自然有序的了。

输出结果:

最好情况下快排每次能恰好均分序列,那么时间复杂度就是O(nlogn),最坏情况下,快排每次划分都只能将序列分为一个元素和其它元素两部分,这时候的快排退化成冒泡排序,时间复杂度为O(n^2)。

插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。

将一个数据插入到 已经排好序的有序数据

第一趟排序:

用数组的第二个数与第一个数( 看成是已有序的数据 )比较

第二趟排序:

用数组的第三个数与已是有序的数据 {2,3} (刚才在第一趟排的)比较

在第二步中:

...

后面依此类推

输出结果:

选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

举例:数组 int[] arr={5,2,8,4,9,1}

第一趟排序 : 原始数据: 5 2 8 4 9 1

最小数据1,把1放在首位,也就是1和5互换位置,

排序结果: 1 2 8 4 9 5

第二趟排序

第1以外的数据 {2 8 4 9 5} 进行比较,2最小,

排序结果: 1 2 8 4 9 5

第三趟排序

除 1、2 以外的数据 {8 4 9 5} 进行比较,4最小,8和4交换

排序结果: 1 2 4 8 9 5

第四趟排序 :

除第 1、2、4 以外的其他数据 {8 9 5} 进行比较,5最小,8和5交换

排序结果: 1 2 4 5 9 8

第五趟排序:

除第 1、2、4、5 以外的其他数据 {9 8} 进行比较,8最小,8和9交换

排序结果: 1 2 4 5 8 9

输出结果:

归并排序(merge sort)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

比如我们对 [8,4,5,7,1,3,6,2] 这个数组进行归并排序,我们首先利用分治思想的“分”将数组拆分。

输出结果:

4. 常用的数据排序算法有哪些,各有什么特点举例结合一种排序算法并应用数组进行数据排序。

排序简介
排序是数据处理中经常使用的一种重要运算,在计算机及其应用系统中,花费在排序上的时间在系统运行时间中占有很大比重;并且排序本身对推动算法分析的发展也起很大作用。目前已有上百种排序方法,但尚未有一个最理想的尽如人意的方法,本章介绍常用的如下排序方法,并对它们进行分析和比较。

1、插入排序(直接插入排序、折半插入排序、希尔排序);
2、交换排序(起泡排序、快速排序);
3、选择排序(直接选择排序、堆排序);
4、归并排序;
5、基数排序;

学习重点
1、掌握排序的基本概念和各种排序方法的特点,并能加以灵活应用;
2、掌握插入排序(直接插入排序、折半插入排序、希尔排序)、交换排序(起泡排序、快速排序)、选择排序(直接选择排序、堆排序)、二路归并排序的方法及其性能分析方法;
3、了解基数排序方法及其性能分析方法。

排序(sort)或分类

所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来。其确切定义如下:
输入:n个记录R1,R2,…,Rn,其相应的关键字分别为K1,K2,…,Kn。
输出:Ril,Ri2,…,Rin,使得Ki1≤Ki2≤…≤Kin。(或Ki1≥Ki2≥…≥Kin)。

1.被排序对象--文件
被排序的对象--文件由一组记录组成。
记录则由若干个数据项(或域)组成。其中有一项可用来标识一个记录,称为关键字项。该数据项的值称为关键字(Key)。
注意:
在不易产生混淆时,将关键字项简称为关键字。

2.排序运算的依据--关键字
用来作排序运算依据的关键字,可以是数字类型,也可以是字符类型。
关键字的选取应根据问题的要求而定。
【例】在高考成绩统计中将每个考生作为一个记录。每条记录包含准考证号、姓名、各科的分数和总分数等项内容。若要惟一地标识一个考生的记录,则必须用"准考证号"作为关键字。若要按照考生的总分数排名次,则需用"总分数"作为关键字。

排序的稳定性

当待排序记录的关键字均不相同时,排序结果是惟一的,否则排序结果不唯一。
在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。
注意:
排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。

排序方法的分类

1.按是否涉及数据的内、外存交换分
在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换,则称之为内部排序(简称内排序);反之,若排序过程中要进行数据的内、外存交换,则称之为外部排序。
注意:
① 内排序适用于记录个数不很多的小文件
② 外排序则适用于记录个数太多,不能一次将其全部记录放人内存的大文件。

2.按策略划分内部排序方法
可以分为五类:插入排序、选择排序、交换排序、归并排序和分配排序。

排序算法分析

1.排序算法的基本操作
大多数排序算法都有两个基本的操作:
(1) 比较两个关键字的大小;
(2) 改变指向记录的指针或移动记录本身。
注意:
第(2)种基本操作的实现依赖于待排序记录的存储方式。

2.待排文件的常用存储方式
(1) 以顺序表(或直接用向量)作为存储结构
排序过程:对记录本身进行物理重排(即通过关键字之间的比较判定,将记录移到合适的位置)

(2) 以链表作为存储结构
排序过程:无须移动记录,仅需修改指针。通常将这类排序称为链表(或链式)排序;

(3) 用顺序的方式存储待排序的记录,但同时建立一个辅助表(如包括关键字和指向记录位置的指针组成的索引表)
排序过程:只需对辅助表的表目进行物理重排(即只移动辅助表的表目,而不移动记录本身)。适用于难于在链表上实现,仍需避免排序过程中移动记录的排序方法。

3.排序算法性能评价
(1) 评价排序算法好坏的标准
评价排序算法好坏的标准主要有两条:
① 执行时间和所需的辅助空间
② 算法本身的复杂程度

(2) 排序算法的空间复杂度
若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间是O(1),则称之为就地排序(In-PlaceSou)。
非就地排序一般要求的辅助空间为O(n)。

(3) 排序算法的时间开销
大多数排序算法的时间开销主要是关键字之间的比较和记录的移动。有的排序算法其执行时间不仅依赖于问题的规模,还取决于输入实例中数据的状态。

文件的顺序存储结构表示

#define n l00 //假设的文件长度,即待排序的记录数目
typedef int KeyType; //假设的关键字类型
typedef struct{ //记录类型
KeyType key; //关键字项
InfoType otherinfo;//其它数据项,类型InfoType依赖于具体应用而定义
}RecType;
typedef RecType SeqList[n+1];//SeqList为顺序表类型,表中第0个单元一般用作哨兵
注意:
若关键字类型没有比较算符,则可事先定义宏或函数来表示比较运算。
【例】关键字为字符串时,可定义宏"#define LT(a,b)(Stromp((a),(b))<0)"。那么算法中"a<b"可用"LT(a,b)"取代。若使用C++,则定义重载的算符"<"更为方便。

按平均时间将排序分为四类:

(1)平方阶(O(n2))排序
一般称为简单排序,例如直接插入、直接选择和冒泡排序;

(2)线性对数阶(O(nlgn))排序
如快速、堆和归并排序;

(3)O(n1+£)阶排序
£是介于0和1之间的常数,即0<£<1,如希尔排序;

(4)线性阶(O(n))排序
如桶、箱和基数排序。

各种排序方法比较

简单排序中直接插入最好,快速排序最快,当文件为正序时,直接插入和冒泡均最佳。

影响排序效果的因素

因为不同的排序方法适应不同的应用环境和要求,所以选择合适的排序方法应综合考虑下列因素:
①待排序的记录数目n;
②记录的大小(规模);
③关键字的结构及其初始状态;
④对稳定性的要求;
⑤语言工具的条件;
⑥存储结构;
⑦时间和辅助空间复杂度等。

不同条件下,排序方法的选择

(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的 排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。

4)在基于比较的排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程。
当文件的n个关键字随机分布时,任何借助于"比较"的排序算法,至少需要O(nlgn)的时间。
箱排序和基数排序只需一步就会引起m种可能的转移,即把一个记录装入m个箱子之一,因此在一般情况下,箱排序和基数排序可能在O(n)时间内完成对n个记录的排序。但是,箱排序和基数排序只适用于像字符串和整数这类有明显结构特征的关键字,而当关键字的取值范围属于某个无穷集合(例如实数型关键字)时,无法使用箱排序和基数排序,这时只有借助于"比较"的方法来排序。
若n很大,记录的关键字位数较少且可以分解时,采用基数排序较好。虽然桶排序对关键字的结构无要求,但它也只有在关键字是随机分布时才能使平均时间达到线性阶,否则为平方阶。同时要注意,箱、桶、基数这三种分配排序均假定了关键字若为数字时,则其值均是非负的,否则将其映射到箱(桶)号时,又要增加相应的时间。
(5)有的语言(如Fortran,Cobol或Basic等)没有提供指针及递归,导致实现归并、快速(它们用递归实现较简单)和基数(使用了指针)等排序算法变得复杂。此时可考虑用其它排序。
(6)本章给出的排序算法,输人数据均是存储在一个向量中。当记录的规模较大时,为避免耗费大量的时间去移动记录,可以用链表作为存储结构。譬如插入排序、归并排序、基数排序都易于在链表上实现,使之减少记录的移动次数。但有的排序方法,如快速排序和堆排序,在链表上却难于实现,在这种情况下,可以提取关键字建立索引表,然后对索引表进行排序。然而更为简单的方法是:引人一个整型向量t作为辅助表,排序前令t[i]=i(0≤i<n),若排序算法中要求交换R[i]和R[j],则只需交换t[i]和t[j]即可;排序结束后,向量t就指示了记录之间的顺序关系:
R[t[0]].key≤R[t[1]].key≤…≤R[t[n-1]].key
若要求最终结果是:
R[0].key≤R[1].key≤…≤R[n-1].key
则可以在排序结束后,再按辅助表所规定的次序重排各记录,完成这种重排的时间是O(n)。

5. 各种排序算法的总结和比较

排序算法是《数据结构与算法》中最基本的算法之一。

排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括:

点击以下图片查看大图:

关于时间复杂度

平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。

线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序;

O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序

线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。

关于稳定性

稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。

不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

名词解释:

n:数据规模 k:"桶"的个数 In-place:占用常数内存,不占用额外内存 Out-place:占用额外内存 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同

包含以下内容:

1、冒泡排序 2、选择排序 3、插入排序 4、希尔排序 5、归并排序 6、快速排序 7、堆排序 8、计数排序 9、桶排序 10、基数排序

排序算法包含的相关内容具体如下:

冒泡排序算法

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

选择排序算法

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n?) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间。

插入排序算法

插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

希尔排序算法

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

归并排序算法

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

快速排序算法

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

堆排序算法

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

计数排序算法

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

桶排序算法

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。

基数排序算法

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

6. 几种排序算法的比较

一、八大排序算法的总体比较

4.3、堆的插入:

每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,然后将这个新数据插入到这个有序数据中

(1)用大根堆排序的基本思想

先将初始数组建成一个大根堆,此对为初始的无序区;

再将最大的元素和无序区的最后一个记录交换,由此得到新的无序区和有序区,且满足<=的值;

由于交换后新的根可能违反堆性质,故将当前无序区调整为堆。然后再次将其中最大的元素和该区间的最后一个记录交换,由此得到新的无序区和有序区,且仍满足关系的值<=的值,同样要将其调整为堆;

..........

直到无序区只有一个元素为止;

4.4:应用

寻找M个数中的前K个最小的数并保持有序;

时间复杂度:O(K)[创建K个元素最大堆的时间复杂度] +(M-K)*log(K)[对剩余M-K个数据进行比较并每次对最大堆进行从新最大堆化]

5.希尔排序

(1)基本思想

先将整个待排序元素序列分割成若干子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序(因为直接插入排序在元素基本有序的情况下,效率很高);

(2)适用场景

比较在希尔排序中是最主要的操作,而不是交换。用已知最好的步长序列的希尔排序比直接插入排序要快,甚至在小数组中比快速排序和堆排序还快,但在涉及大量数据时希尔排序还是不如快排;

6.归并排序

(1)基本思想

首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2的有序子序列,在此基础上,再对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列,以此类推,直到得到一个长度为n的有序序列为止;

(2)适用场景

若n较大,并且要求排序稳定,则可以选择归并排序;

7.简单选择排序

(1)基本思想

第一趟:从第一个记录开始,将后面n-1个记录进行比较,找到其中最小的记录和第一个记录进行交换;

第二趟:从第二个记录开始,将后面n-2个记录进行比较,找到其中最小的记录和第2个记录进行交换;

...........

第i趟:从第i个记录开始,将后面n-i个记录进行比较,找到其中最小的记录和第i个记录进行交换;

以此类推,经过n-1趟比较,将n-1个记录排到位,剩下一个最大记录直接排在最后;

阅读全文

与各个排序算法适用情况和长处相关的资料

热点内容
dvd光盘存储汉子算法 浏览:758
苹果邮件无法连接服务器地址 浏览:963
phpffmpeg转码 浏览:672
长沙好玩的解压项目 浏览:145
专属学情分析报告是什么app 浏览:564
php工程部署 浏览:833
android全屏透明 浏览:737
阿里云服务器已开通怎么办 浏览:803
光遇为什么登录时服务器已满 浏览:302
PDF分析 浏览:486
h3c光纤全工半全工设置命令 浏览:143
公司法pdf下载 浏览:383
linuxmarkdown 浏览:350
华为手机怎么多选文件夹 浏览:683
如何取消命令方块指令 浏览:350
风翼app为什么进不去了 浏览:779
im4java压缩图片 浏览:362
数据查询网站源码 浏览:151
伊克塞尔文档怎么进行加密 浏览:893
app转账是什么 浏览:163