常用排序总结——比较排序

  • 常说的排序一般指内部排序算法,即记录在内存中进行排序
  • 排序大体分为两种:
  • 比较排序,时间复杂度O(nlogn)~O(n^2),主要有:冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序
  • 非比较排序,时间复杂度:O(n),主要有:基数排序、计数排序、桶排序
  • 排序算法的稳定性

排序算法的稳定性简单定义为:如果Ai = Aj,排序前Ai在Aj前,排序后Ai还在Aj前,则称这种排序算法是稳定的。即保证排序前两个相等的数相对顺序不变。

排序算法的稳定性是由具体算法定的,在某种条件下,稳定的可变不稳定,不稳定也可变稳定

比如冒泡排序,如果把交换条件加一个等号,就会从稳定的排序变成不稳定的排序

 

  • 冒泡排序(bubblesort)

入门排序必备,当年领我们入门的学长还把冒泡排序和选择排序搞混了=。=

依次比较相邻两个元素,如果顺序错了就调换一下,直到没有元素需要交换

最差时间复杂度:O(n^2)

最优时间复杂度:O(n)  

平均时间复杂度:O(n^2)

稳定性:稳定

//基础版
var
        n               :longint;
        i,j             :longint;
        a               :array[0..10010] of longint;
procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   for i:=1 to n do
     for j:=1 to n-i do
       if a[j]>a[j+1] then swap(a[j],a[j+1]);
   for i:=1 to n do write(a[i], ' ');writeln;

end.

冒泡排序有很多细节优化

优化一:排序完成就退出排序,设置一个flag判断是否已经有序

var
        n,len           :longint;
        i               :longint;
        a               :array[0..10010] of longint;
        flag            :boolean;
procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   flag:=true; len:=n;
   while flag do
   begin
      flag:=false;
      for i:=1 to len-1 do
        if a[i]>a[i+1] then
        begin
           swap(a[i],a[i+1]);
           flag:=true;
        end;
      dec(len);
   end;
   for i:=1 to n do write(a[i], ' ');writeln;
end.

优化二:优化一保证了循环次数最优,但是对于有序部分仍需要比较。那么就可以在优化一的基础上设置一个边界,减少比较次数

var
        i               :longint;
        flag,n,len      :longint;
        a               :array[0..10010] of longint;
procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   len:=n; flag:=n;
   while flag>0 do
   begin
      flag:=0;
      for i:=1 to len-1 do
        if a[i]>a[i+1] then
        begin
           swap(a[i],a[i+1]);
           flag:=i+1;
        end;
      len:=flag;
   end;
   for i:=1 to n do write(a[i],' ');writeln;
end.

优化三:定向冒泡排序,又名鸡尾酒排序,排序方法是从低到高再从高到低(冒泡排序只是从低到高比较每个元素)

var
        n,l,r           :longint;
        i               :longint;
        a               :array[0..10010] of longint;

procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   l:=1; r:=n;
   while (l<r) do
   begin
      for i:=l to r-1 do
        if a[i]>a[i+1] then swap(a[i],a[i+1]);
      dec(r);
      for i:=r downto l+1 do
        if a[i]<a[i-1] then swap(a[i],a[i-1]);
      inc(l);
   end;
   for i:=1 to n do write(a[i],' ');writeln;
end.

但是,乱序下,不管怎么优化,冒泡排序的效率依旧很差劲눈_눈

  • 选择排序(select sort)

简单直观:每次找最小(大)的放到首(尾),再在剩余未排序的元素中继续找最小(大)

最差时间复杂度:O(n^2)

最优时间复杂度:O(n^2)  

平均时间复杂度:O(n^2)

稳定性:不稳定

var
        n,min,tt        :longint;
        i,j             :longint;
        a               :array[0..10010] of longint;
procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   for i:=1 to n-1 do
   begin
      min:=maxlongint;
      for j:=i to n do
        if a[j]<min then
        begin
           min:=a[j]; tt:=j;
        end;
      if min<>maxlongint then swap(a[tt],a[i]); //可能打乱稳定性,所以选择排序是不稳定的
   end;
   for i:=1 to n do write(a[i],' ');writeln;
end.
  • 快速排序(quick sort)

快排利用的是分治策略:选出一个基准,把比基准小的放基准前,把比基准小的放基准后面,对每个分区递归进行上述操作。

最差时间复杂度:O(n^2)(每次选的基准都是最小或最大的元素,需要进行n-1次划分)

最优时间复杂度:O(nlogn)  (每次选的基准都是中位数,只需要logn次划分)

平均时间复杂度:O(nlogn)

稳定性:不稳定

var
        n               :longint;
        i               :longint;
        a               :array[0..10010] of longint;

procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

procedure qs(l,r:longint);
var
        x:longint;
        i,j:longint;
begin
   i:=l; j:=r; x:=a[(l+r)>>1];
   while (i<=j) do
   begin
      while a[i]<x do inc(i);
      while a[j]>x do dec(j);
      if (i<=j) then
      begin
         swap(a[i],a[j]);
         inc(i); dec(j);
      end;
   end;
   if i<r then qs(i,r);
   if j>l then qs(l,j);
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   qs(1,n);
   for i:=1 to n do write(a[i], ' ');writeln;
end.
  • 归并排序(merge sort)

归并排序的实现分为递归实现(分治)和非递归实现(合并)

 

最差时间复杂度:O(nlogn)

最优时间复杂度:O(nlogn)  

平均时间复杂度:O(nlogn)

稳定性:稳定

var
        i               :longint;
        n               :longint;
        a,c             :array[0..10010] of longint;

procedure merge_sort(l,r,mid:longint);
var
        i,j,t:longint;
begin
   i:=l; j:=mid+1; t:=l;
   while (t<=r) do
   begin
      if (i<=mid) and ((a[i]<=a[j]) or (j>r)) then
      begin
         c[t]:=a[i];
         inc(i);
         inc(t);
      end else
      begin
         c[t]:=a[j];
         inc(j);
         inc(t);
      end;
   end;
   for i:=l to r do a[i]:=c[i];
end;

procedure merge(l,r:longint);
var
        mid:longint;
begin
   if l=r then exit;
   mid:=(l+r)>>1;
   merge(l,mid);
   merge(mid+1,r);
   merge_sort(l,r,mid);
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   merge(1,n);
   for i:=1 to n do write(a[i],' ');writeln;
end.
  • 堆排序(heap sort)

最差时间复杂度:O(nlogn)

最优时间复杂度:O(nlogn)  

平均时间复杂度:O(nlogn)

稳定性:不稳定

var
        n,x,size        :longint;
        i               :longint;
        heap            :array[0..10010] of longint;

procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

procedure heap_down(x:longint);
var
        t:longint;
begin
   while ((x<<1)<=size) do
   begin
      if heap[x<<1]<heap[x] then t:=x<<1 else t:=x;
      if (x<<1)+1<=size then
        if heap[(x<<1)+1]<heap[t] then t:=(x<<1)+1;
      if x<>t then
      begin
         swap(heap[x],heap[t]);
         x:=t;
      end else break;
   end;
end;

procedure heap_up(x:longint);
begin
   if x=1 then exit;
   while x<>1 do
   begin
      if heap[x]<heap[x>>1] then
      begin
         swap(heap[x],heap[x>>1]);
         x:=x>>1;
      end else break;
   end;
end;

begin
   read(n); size:=0;
   for i:=1 to n do
   begin
      read(x);
      inc(size);
      heap[size]:=x;
      heap_up(size);
   end;
   while (size>0) do
   begin
      write(heap[1],' ');
      heap[1]:=heap[size];
      dec(size);
      heap_down(1);
   end;
end.

  • 插入排序(insertion sort)

插入排序的原理类似于我们玩扑克牌的时候理牌或者站队的时候老师按大小个排队时给同学调整位置

对于未排序序列,在排好的序列中从后往前扫,找到正确位置插入

从第二个元素开始(默认第一个元素已有序)作为新元素,从后向前扫有序序列,如果有序数列中的元素大于新元素,则将它向后移一个位置,直到在有序数列中找到小于或等于新元素的,把新元素插入到它后面。

最差时间复杂度:O(n^2)(原数列降序排列)

最优时间复杂度:O(n)  (原数列升序排列)

平均时间复杂度:O(n^2)

稳定性:稳定

优化一:常用二分查找优化比较次数,称为二分插入排序

最差时间复杂度:O(n^2)

最优时间复杂度:O(nlogn)  

平均时间复杂度:O(n^2)

稳定性:稳定

var
        n,t,l,r,mid     :longint;
        i,j             :longint;
        a               :array[0..10010] of longint;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   for i:=2 to n do
   begin
      l:=1; r:=i-1; t:=a[i];
      while (l<=r) do
      begin
         mid:=(l+r)>>1;
         if a[mid]>t then r:=mid-1 else l:=mid+1;
      end;
      for j:=i-1 downto l do a[j+1]:=a[j];
      a[l]:=t;
   end;
   for i:=1 to n do write(a[i],' ');writeln;
end.

优化二:希尔排序(shell sort),又名递减增量排序

希尔排序取一个步长,这样比较交换时可以让元素一次性朝最终位置前进一大步(优化插入排序每次只移一步),再取越来越小的步长,直到最后一步又变成普通的插入排序,但此时序列已接近升序

最差时间复杂度:O(n(logn)^2)

最优时间复杂度:O(n)  

平均时间复杂度:取决于所取步长

稳定性:不稳定(在各自排序中相等顺序可能打乱)

var
        n,tt,t          :longint;
        i,j             :longint;
        a               :array[0..10010] of longint;
begin
   read(n);
   for i:=1 to n do read(a[i]);
   tt:=0;
   while tt<=n do tt:=tt*3+1; //确定初始步长
   while (tt>=1) do
   begin
      for i:=tt+1 to n do
      begin
         j:=i-tt;  t:=a[i];
         while (j>0) and (a[j]>t) do
         begin
            a[j+tt]:=a[j];
            j:=j-tt;
         end;
         a[j+tt]:=t;
      end;
      tt:=(tt-1) div 3;
   end;
   for i:=1 to n do write(a[i], ' ');writeln;
end.

——Eirlys

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值