各种排序模板

1、冒泡排序

重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。
冒泡排序算法的原理如下:

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  • 针对所有的元素重复以上的步骤,除了第一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
    时间复杂度:
    最好 O(n) 正序的情况,一趟扫描即可完成排序
    最差:O(n^2)逆序
    平均 O(n^2)
    稳定
 void Bubble_Sort(vector<int> &vec)
 {
     for(int i = 0; i < vec.size() -1; i ++)
     {
         bool flag=false;
         for(int j = 0; j < vec.size() - i - 1; j ++)
         {
             if(vec[j] > vec[j + 1])
             {
                swap(vec[j],vec[j + 1]);
                flag=true;
             }
         }
         if(!flag) break;
     }
 }
2、选择排序

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

  • 初始状态:无序区为R[1…n],有序区为空;
  • 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
  • n-1趟结束,数组有序化了。
    时间复杂度:
    最好 O(n^2)
    最差 O(n^2)
    平均 O(n^2)
    不稳定
 void Seletion_Sort(vector<int> &vec)
{
    for(int i = 0; i < vec.size(); i ++)
    {
        for(int j = i + 1; j < vec.size(); j ++)
        {
            if(vec[i] > vec[j])  swap(vec[i],vec[j]);
        }
    }
}
//可优化,减少交换次数
 ```java
 void Seletion_Sort2(vector<int> &vec)
{
    int k= 0;
    for(int i = 0; i < vec.size() - 1; i ++)
    {
        k = i;
        for(int j = i + 1; j < vec.size(); j ++)
        {
            if(vec[j] < vec[k])  k = j;
        }
        if(i != k)  swap(vec[i], vec[k]);
    }
}
3、插入排序
3.1、直接插入排序

直接插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

  • 从第一个元素开始,该元素可以认为已经被排序;
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置,将新元素插入到该位置后;
  • 重复步骤2~5。
    时间复杂度:
    最好 O(n)
    最差 O(n^2)
    平均 O(n^2)
    稳定
  void Insertion_Sort(vector<int> &vec)
 {
     for(int i = 1; i < vec.size(); i ++)
     {
         int temp = vec[i],j;
         for(j = i - 1; j >= 0; j --)
         {
             if(temp < vec[j]) vec[j + 1]=vec[j];
             else break;
         }
         vec[j + 1] = temp;
     }
 }
3.2二分插入排序

原理:通过二分查找法找到待插入点,确定位置后,将整个序列后移,并将元素插入到相应位置。
折半查找只是减少了比较次数,但是元素的移动次数不变,所以时间复杂度为O(n^2)

void Binary_Insertion_Sort(int a[],int n)
{
  for(int i = 1; i < n; i ++)
  {
     int l = 0, r = i - 1, temp = a[i], j;
     while(l <= r)
     {
         int mid = l + r >> 1;
         if(a[mid] < temp) l = mid + 1;
         else r = mid - 1;
     }
     for(j = i - 1; j >= l; j --) a[j + 1] = a[j];
     a[j + 1] = temp;
  }
}
3.3、希尔排序

又称缩小增量排序,基本思想是先将整个待排序记录序列分割成若干个子序列,然后对每个子序列分别进行直接插入排序,待整个序列基本有序时,在对全体记录进行一次直接插入排序。(一般的初次取序列的一半为增量,以后每次减半,直到增量为1。)
时间复杂度:
最好 O(n)
最差 O(n^2)
平均 O(n^1.3)
不稳定

 void Shell_Sort(vector& vec)
 {
     //掌握思想即可
 }
4、归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2路归并。
算法流程:
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
归并排序最坏,最好,平均时间复杂度均为O(nlogn),空间复杂度是O(n),它是稳定排序。

void Merge_Sort(vector<int> &vec,int l,int r)
{
  if(l >= r) return;
  int mid=(l + r) / 2;
  Merge_Sort(vec,l,mid);
  Merge_Sort(vec,mid + 1,r);
//声明一个全局变量。所有的函数共用一个变量。
  static vector<int> t;
  t.clear();
  int i = l, j = mid + 1;
  while(i <= mid && j <= r) {
   if(vec[i] <= vec[j]) t.push_back(vec[i ++ ]);
   else t.push_back(vec[j ++ ]);
  }
  while(i <= mid) t.push_back(vec[i ++ ]);
  while(j <= r) t.push_back(vec[j ++ ]);
  for(i = l, j = 0; j < t.size(); i ++,j ++) vec[i] = t[j];
}
5、快速排序

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
    时间复杂度:
    最好O(nlogn)
    最差:O(n^2) 在序列已经排好序的情况
    平均 O(nlogn)
    空间复杂度:
    最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
    最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况
    不稳定
    为什么不稳定?
    假设A,B相等,假设此时的快排是小于等于关键字为排在前面的一组组,大于为另外排在后面的一组.
    在选取一个数出来分组的时候,如果选到了A为基准数,那么在B<=A的情况下,B将会排在A的前面.
    Q:不稳定的排序算法有哪些?
    A:编程累了? 些(希尔) 选择 朋友去玩啊~
  void Quick_Sort(vector<int> &vec, int l, int r)
 {
     if(l >= r) return;
     int i = l - 1, j = r + 1,x = vec[(l + r) / 2];
     while(i < j)
     {
         do i ++; while(vec[i] < x);
         do j --; while(vec[j] > x);
         if(i < j) swap(vec[i],vec[j]);
         else 
         {
         Quick_Sort(vec, l, j);
         Quick_Sort(vec, j + 1, r);
         }
     }
 }
6、堆排序

堆排序是一种选择排序
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
步骤:

  • 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
  • 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  • 将剩余n-1个元素重新构造成一个堆,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
    堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
 void push_down(vector<int> &heap, int size, int u)
 {
     int t = u, left = u * 2, right = u * 2 + 1;
     if(left <= size && heap[left] > heap[t]) t = left;
     if(right <= size && heap[right] > heap[t]) t = right;
     if(t != u)
     {
         swap(heap[t], heap[u]);
         push_down(heap, size, t);
     }
 }
 void push_up(vector<int> &heap, int u)
 {
     while(u / 2 &&heap[u / 2] < heap[u])
     {
         swap(heap[u / 2], heap[u]);
         u /= 2;
     }
 }
  void Heap_Sort(vector<int> &q, int n)
  {
      int size = n;
      for(int i = 1; i <= n; i ++) push_up(q, i);
      //此时是一个大根堆,最大的元素在堆顶。
      for(int i = 1; i <= n; i ++)
      {
           swap(q[1], q[size]);
           size --;
           push_down(q, size, 1);
      }
  }
//附加
 /* 在堆中插入一个元素
 void insert(vector<int> &heap, int size, int x)
    {
     heap[ ++ size] = x;
     push_up(heap, size, x);
}
//删除堆顶元素
void remove_top(vector<int> &heap, int &size)
{
    heap[1] = heap[size];
    size --;
    push_down(heap, size, 1);
}
*/

7-9排序方法都是线性时间非比较类排序,且都是稳定排序

7、计数排序

计数排序是一种非常快捷的稳定性强的排序方法,时间复杂度O(n+k),空间复杂度也是O(n+k)其中n为要排序的数的个数,k为要排序的数的组大值。计数排序对一定量的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序。计数排序是消耗空间发杂度来获取快捷的排序方法,其空间发展度为O(K)同理K为要排序的最大值。
原理:计数排序算法没有用到元素间的比较,它利用元素的实际值来确定它们在输出数组中的位置。

void Counting_Sort(vector<int> &nums, int n)
{
    vector<int> cnt(100, 0);
    for(int i = 0; i < n; i ++) cnt[nums[i]] ++;
    //cut[0]~cnt[99]中的数值其实就是0到99每个元素出现的次数。
    for(int j = 0, k = 0; j < 100; j ++)
    {
      while(cnt[j])
      {
          nums[k ++] = j;
          cnt[j] --;
      }
    }
}
8、桶排序

桶排序是计数排序的变种。工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法(快速排序)或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来记得到有序序列。
基本思想:
桶排序的思想近乎彻底的分治思想。
桶排序假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。
接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列。
实现逻辑:

  • 设置一个定量的数组当作空桶子。
  • 寻访序列,并且把项目一个一个放到对应的桶子去。
  • 对每个不是空的桶子进行排序。
  • 从不是空的桶子里把项目再放回原来的序列中。
void Bucket_Sort(vector<int> &q)
{
    //掌握思想即可
}
9、基数排序

基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
实现:
以LSD(低位优先)为例

  • 首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中。
  • 接下来将这些桶子中的数值重新串接起来。
  • 接着再进行一次分配,这次是根据十位数来分配。
  • 接下来将这些桶子中的数值重新串接起来,成为以下的数列。
  • 这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
int get(int x, int i)
{
    while(i -- ) x /= 10;
    return x % 10;
}
void Radix_Sort(vector<int> &q, int n)
{
   vector<vector<int> > cnt(10);
   for(int i = 0; i < 3; i ++)
   {
     for(int j = 0; j < 10; j ++) cnt[j].clear();
     for(int j = 0; j < n; j ++) cnt[get(q[j], i)].push_back(q[j]);
     for(int j = 0, k = 0; j < 10; j ++)
     {
       for(int x : cnt[j]) q[k ++] = x;
     }
   }
}
int main()
{
  vector<int> vec;
  int n;
  cin>>n;
  vec.resize(n+1);
  for(int i = 1; i <= n; i ++) cin>>vec[i];
  // Merge_Sort(vec,0,vec.size()-1);
  // Bubble_Sort(vec);
  // Seletion_Sort(vec);
  // Insertion_Sort(vec);
  // Quick_Sort(vec,0,vec.size()-1);
  // Heap_Sort(vec,n);  //索引从1开始
  // Counting_Sort(vec,n);
  Radix_Sort(vec,n);
  for(int i = 1;i <= n;i++) cout<<vec[i]<<endl;
  return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值