排序大全

一、插入排序(Insertion Sort)
1. 基本思想:
     每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
2. 排序过程:  
 【示例】:
[初始关键字] [49] 38 65 97 76 13 27 49
    J=2(38) [38 49] 65 97 76 13 27 49
    J=3(65) [38 49 65] 97 76 13 27 49
    J=4(97) [38 49 65 97] 76 13 27 49
    J=5(76) [38 49 65 76 97] 13 27 49
    J=6(13) [13 38 49 65 76 97] 27 49
    J=7(27) [13 27 38 49 65 76 97] 49
    J=8(49) [13 27 38 49 49 65 76 97] 
Procedure InsertSort(Var R : FileType);
//对R[1..N]按递增序进行插入排序, R[0]是监视哨//
  Begin
    for I := 2 To N Do      //依次插入R[2],...,R[n]//
    begin
      R[0] := R[I]; J := I - 1;
      While R[0] < R[J] Do   //查找R[I]的插入位置//
       begin
        R[J+1] := R[J];       //将大于R[I]的元素后移//
        J := J - 1
       end
      R[J + 1] := R[0] ;      //插入R[I] //
    end
  End;                  //InsertSort //

二、选择排序
1. 基本思想:
  每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
2. 排序过程:
 【示例】:
  初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 〔38 65 97 76 49 27 49]
第二趟排序后 13 27 〔65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最后排序结果 13 27 38 49 49 76 76 97
Procedure SelectSort(Var R : FileType);      //对R[1..N]进行直接选择排序 //
  Begin
    for I := 1 To N - 1 Do                  //做N - 1趟选择排序//
     begin
      K := I;
      For J := I + 1 To N Do                //在当前无序区R[I..N]中选最小的元素R[K]//
       begin
        If R[J] < R[K] Then K := J
       end;
      If K <> I Then                      //交换R[I]和R[K] //
        begin Temp := R[I]; R[I] := R[K]; R[K] := Temp; end;
     end
  End.                               //SelectSort //

三、冒泡排序(BubbleSort)
1. 基本思想:
  两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
2. 排序过程:
  设想被排序的数组R〔1..N〕垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。
 【示例】:
49 13 13 13 13 13 13 13 
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97 
Procedure BubbleSort(Var R : FileType) //从下往上扫描的起泡排序//
Begin
  For I := 1 To N-1 Do   //做N-1趟排序//
   begin
     NoSwap := True;    //置未排序的标志//
     For J := N - 1 DownTo 1 Do    //从底部往上扫描//
      begin
       If R[J+1]< R[J] Then    //交换元素//
        begin
         Temp := R[J+1]; R[J+1 := R[J]; R[J] := Temp;
         NoSwap := False
        end;
      end;
     If NoSwap Then Return    //本趟排序中未发生交换,则终止算法//
四.合并排序
1.基本思想
   合并排序的算法就是二分法。
   分解:将n个元素分解成各含一半元素的子序列。 
   解决:用合并排序法对两个子序列递归地排序。
   合并:合并两个已排序的子序列排序结果。
   在对子序列排列时,当其长度为1时递归结束,因为单个元素被认为是已排好序的.合并排序的.合并排序的关键步骤在于合并目前产生的两个已排好序的子序列:
A[p..q] 和 A[q+1…r];
   将它们合并成一个已排好序的子序列A[p..r]. 我们引入一个辅助过程merge(A,p,q,r)来完成这一项合并工作,其中A是数组,p,q,r是下标.
    2. 算法实现           
procedure merge( p,q,r:integer);
var
  i,j,t:integer;
  it:array[1..10] of integer;
begin
  t:=p; i:=p; j:=q+1;
  while t<=r do
   begin
     if (i<=q) and ((j>j) or (a[i]<=a[j]))
       then begin
              it[t]:=a[i]; inc(i);
            end
       else begin
              it[t]:=a[j]; inc(j); 
            end;
     inc(t);
    end;
 for i:=p to r do a[i]:=t[i];
end;
procedure merge_sort(p,r:integer);
 var q:integer;
 begin
  if p<>r then begin
                 q:=(p+r-1) div 2 ;
                 merge_sort(p,q);
                 merge_sort(q+1,r); 
                 merge(p,q,r); 
               end;  
 end;
begin
  merge_sort(1,10); 
end.
五、快速排序(Quick Sort)
1. 基本思想:
  在当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
2. 排序过程:
 【示例】:
初始关键字 [49 38 65 97 76 13 27 49〕
第一次交换后 〔27 38 65 97 76 13 49 49〕 
第二次交换后 〔27 38 49 97 76 13 65 49〕 
J向左扫描,位置不变,第三次交换后 〔27 38 13 97 76 49 65 49〕 
I向右扫描,位置不变,第四次交换后 〔27 38 13 49 76 97 65 49〕
J向左扫描 〔27 38 13 49 76 97 65 49〕
(一次划分过程) 
初始关键字 〔49 38 65 97 76 13 27 49〕
一趟排序之后 〔27 38 13〕 49 〔76 97 65 49〕 
二趟排序之后 〔13〕 27 〔38〕 49 〔49 65〕76 〔97〕
三趟排序之后 13 27 38 49 49 〔65〕76 97
最后的排序结果 13 27 38 49 49 65 76 97 
各趟排序之后的状态
Procedure Parttion(Var R : FileType; L, H : Integer; Var I : Integer);
//对无序区R[1,H]做划分,I给以出本次划分后已被定位的基准元素的位置 //
Begin
  I := 1; J := H; X := R[I] ;   //初始化,X为基准//
  Repeat
    While (R[J] >= X) And (I < J) Do
      begin
       J := J - 1        //从右向左扫描,查找第1个小于 X的元素//
       If I < J Then     //已找到R[J] 〈X//
         begin
          R[I] := R[J];   //相当于交换R[I]和R[J]//
          I := I + 1
         end;
       While (R[I] <= X) And (I < J) Do
          I := I + 1      //从左向右扫描,查找第1个大于 X的元素///
      end;
     If I < J Then    //已找到R[I] > X //
       begin         R[J] := R[I];     //相当于交换R[I]和R[J]//
        J := J - 1
       end
  Until I = J;
  R[I] := X       //基准X已被最终定位//
Procedure QuickSort(Var R :FileType; S,T: Integer); //对R[S..T]快速排序//
Begin
  If S < T Then    //当R[S..T]为空或只有一个元素是无需排序//
    begin
      Partion(R, S, T, I);    //对R[S..T]做划分//
      QuickSort(R, S, I-1);  //递归处理左区间R[S,I-1]//
      QuickSort(R, I+1,T);  //递归处理右区间R[I+1..T] //
    end;
End.                  //QuickSort//

六、堆排序(Heap Sort)
1. 基本思想:
     堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
2. 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:
       Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])
 
     堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等于其孩子的关键字,则称之为大根堆。
3. 排序过程:
    堆排序正是利用小根堆(或大根堆)来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐步向前扩大到整个记录区。
 【示例】:对关键字序列42,13,91,23,24,16,05,88建堆 
   
  
Procedure Sift(Var R :FileType; I, M : Integer);
//在数组R[I..M]中调用R[I],使得以它为完全二叉树构成堆。事先已知其左、右子树(2I+1 <=M时)均是堆//
Begin
  X := R[I]; J := 2*I;            //若J <=M, R[J]是R[I]的左孩子//
  While J <= M Do            //若当前被调整结点R[I]有左孩子R[J]//
   begin
    If (J < M) And R[J].Key < R[J+1].Key Then
      J := J + 1                 //令J指向关键字较大的右孩子//
                                //J指向R[I]的左、右孩子中关键字较大者//
    If X.Key < R[J].Key Then     //孩子结点关键字较大//
      begin
        R[I] := R[J];              //将R[J]换到双亲位置上//
        I := J ; J := 2*I            //继续以R[J]为当前被调整结点往下层调整//
      end;
     Else
      Exit                  //调整完毕,退出循环//
   end
  R[I] := X;               //将最初被调整的结点放入正确位置//
End;//Sift//
Procedure HeapSort(Var R : FileType);        //对R[1..N]进行堆排序//
 Begin
  For I := N Div Downto 1 Do               //建立初始堆//
   Sift(R, I , N)
  For I := N Downto 2 do                  //进行N-1趟排序//
   begin
    T := R[1]; R[1] := R[I]; R[I] := T;     //将当前堆顶记录和堆中最后一个记录交换//
    Sift(R, 1, I-1)                 //将R[1..I-1]重成堆//
   end
 End;                 //HeapSort//

七、几种排序算法的比较和选择 
1. 选取排序方法需要考虑的因素:
(1) 待排序的元素数目n;
(2) 元素本身信息量的大小;
(3) 关键字的结构及其分布情况;
(4) 语言工具的条件,辅助空间的大小等。
2. 小结:
(1) 若n较小(n <= 50),则可以采用直接插入排序或直接选择排序。由于直接插入排序所需的记录移动操作较直接选择排序多,因而当记录本身信息量较大时,用直接选择排序较好。
(2) 若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。
(3) 若n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。 快速排序是目前基于比较的内部排序法中被认为是最好的方法。
(4) 在基于比较排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程,由此可以证明:当文件的n个关键字随机分布时,任何借助于"比较"的排序算法,至少需要O(nlog2n)的时间。
(5) 当记录本身信息量较大时,为避免耗费大量时间移动记录,可以用链表作为存储结构。

箱排序(Bin Sort)
1、箱排序的基本思想
     箱排序也称桶排序(Bucket Sort),其基本思想是:设置若干个箱子,依次扫描待排序的记录R[0],R[1],…,R[n-1],把关键字等于k的记录全都装入到第k个箱子里(分配),然后按序号依次将各非空的箱子首尾连接起来(收集)。
【例】要将一副混洗的52张扑克牌按点数A<2<…<J<Q<K排序,需设置13个"箱子",排序时依次将每张牌按点数放入相应的箱子里,然后依次将这些箱子首尾相接,就得到了按点数递增序排列的一副牌。
2、箱排序中,箱子的个数取决于关键字的取值范围。
     若R[0..n-1]中关键字的取值范围是0到m-1的整数,则必须设置m个箱子。因此箱排序要求关键字的类型是有限类型,否则可能要无限个箱子。
3、箱子的类型应设计成链表为宜
     一般情况下每个箱子中存放多少个关键字相同的记录是无法预料的,故箱子的类型应设计成链表为宜。
4、为保证排序是稳定的,分配过程中装箱及收集过程中的连接必须按先进先出原则进行。
(1) 实现方法一
     每个箱子设为一个链队列。当一记录装入某箱子时,应做人队操作将其插入该箱子尾部;而收集过程则是对箱子做出队操作,依次将出队的记录放到输出序列中。
(2) 实现方法二
     若输入的待排序记录是以链表形式给出时,出队操作可简化为是将整个箱子链表链接到输出链表的尾部。这只需要修改输出链表的尾结点中的指针域,令其指向箱子链表的头,然后修改输出链表的尾指针,令其指向箱子链表的尾即可。
5、算法简析
     分配过程的时间是O(n);收集过程的时间为O(m) (采用链表来存储输入的待排序记录)或O(m+n)。因此,箱排序的时间为O(m+n)。若箱子个数m的数量级为O(n),则箱排序的时间是线性的,即O(n)。
  注意:箱排序实用价值不大,仅适用于作为基数排序(下节介绍)的一个中间步骤。

基数排序  
   基数排序(Radix Sort)是对箱排序的改进和推广。
1、单关键字和多关键字
     文件中任一记录R[i]的关键字均由d个分量构成。若这d个分量中每个分量都是一个独立的关键字,则文件是多关键字的(如扑克牌有两个关键字:点数和花色);否则文件是单关键字的, (0≤j<d)只不过是关键字中其中的一位(如字符串、十进制整数等)。
    多关键字中的每个关键字的取值范围一般不同。如扑克牌的花色取值只有4种,而点数则有13种。单关键字中的每位一般取值范围相同。

2、基数
      设单关键字的每个分量的取值范围均是:
      C0≤kj≤Crd-1(0≤j<d)
可能的取值个数rd称为基数。
     基数的选择和关键字的分解因关键宇的类型而异:
(1) 若关键字是十进制整数,则按个、十等位进行分解,基数rd=10,C0=0,C9=9,d为最长整数的位数;
(2) 若关键字是小写的英文字符串,则rd=26,Co='a',C25='z',d为字符串的最大长度。
3、基数排序的基本思想
     基数排序的基本思想是:从低位到高位依次对Kj(j=d-1,d-2,…,0)进行箱排序。在d趟箱排序中,所需的箱子数就是基数rd,这就是"基数排序"名称的由来。
4、基数排序的排序过程
     要排序的记录关键字取值范围是0到99之间的整数(36,5,16,98,95,47, 32,36,48)。对这些关键字进行基数排序的过程。
5、基数排序的类型说明和算法描述
     要保证基数排序是正确的,就必须保证除第一趟外各趟箱排序是稳定的。。
6、算法分析
     若排序文件不是以数组R形式给出,而是以单链表形式给出(此时称为链式的基数排序),则可通过修改出队和人队函数使表示箱子的链队列无须分配结点空间,而使用原链表的结点空间。人队出队操作亦无需移动记录而仅需修改指针。虽然这样一来节省了一定的时间和空间,但算法要复杂得多,且时空复杂度就其数量级而言并未得到改观。 
     基数排序的时间是线性的(即O(n))。
     基数排序所需的辅助存储空间为O(n+rd)。
     基数排序是稳定的。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值