浅谈归并排序

 

本文已彻底 沦为 变成博主的习题册,清转头阅读其他文章哦~啾咪~

 

 


 

参考了文章:https://blog.csdn.net/qq_41550842/article/details/81215935

归并排序的理解

归并排序

(图源见水印)

归并排序,可以看这个网站的动图理解:https://visualgo.net/zh/sorting?slide=10-2

归并排序是分治算法的一种体现,分治解题的一般步骤是:

  1. 划分步骤:将大的原始问题划分成较小的子问题并递归地解决较小的子问题,
  2. 解决步骤:结合较小的子问题的结果来产生较大的原始问题的结果。

而归并排序的步骤可以细化为:

  1. 划分问题:把序列分成元素个数尽量相等的两半
  2. 递归求解:把两半元素分别排序
  3. 合并问题:把两个有序表合并为一个

在合并两边已排好的元素时,因为两个序列都具有不下降性,所以只需要比较两个序列里最小的元素就可以。 

对于实现,一般需要一个辅助数组;

 

#include<bits/stdc++.h>
using namespace std;
const int N =  1000005;
int a[N] ,b[N];//b为辅助数组
void merge_sort(int l , int r)
{
    if(l < r){ //如果整个区间中元素个数大于1,则继续分割
        int mid = (l + r) >> 2 ;  //位运算提高速度
        int i = l; //辅助数组的下标
        int p = l , q = mid+1;
        merge_sort(l,mid);
        merge_sort(mid+1 ,r);
        while(p<=mid || q<=r){//左右两部分只要有一部分不为空
            if(q>r || (p<=mid && a[p]<=a[q]))//右边空了或者右边的数更大一点,就把左边的这个数放到前面一点点,(从左半数组复制到辅助数组)
                b[i++] = a[p++];
            else b[i++] = a[q++];//从右半数组复制到辅助数组
        }
        for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
            a[i] = b[i];
    }
}
int main()
{
     int n;
     scanf("%d",&n);
     for(int i = 1 ; i <= n; i ++)  cin >> a[i];
     merge_sort(1 , n);
     for(int i = 1; i <= n; i++) cout << a[i] << " ";
     return 0;
}
View Code

 

 

 

归并排序的应用

1.求逆序对

(听说可以用树状数组,然鹅小蒟蒻还没有学,学了以后再补一个叭qwq)

在比较的时候考虑:如果左边最小的数都比右边这个数大,那么左边剩余的所有数都会比右边这个数大 

 

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N]; 
long long cnt;
void merge_sort(int l , int r)
{
    if(l < r){
        int mid = (l + r) / 2 ;
        int i = l; 
        int p = l , q = mid+1;
        merge_sort(l , mid);
        merge_sort(mid+1 , r);
        while(p<=mid || q<=r){
            if(q > r || (p <= mid && a[p] <= a[q])) b[i++] = a[p++];
            else{
                b[i++] = a[q++];
                cnt += mid -p +1;  //将逆序对的个数累加起来
            }
        }
        for(i = l ; i <= r; i++) a[i] = b[i];
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1 ; i <= n; i ++) cin >> a[i];
    cnt = 0;
    merge_sort(1 , n);
    printf("%lld",cnt);
    return 0;
}
 
求逆序对

[求逆序对·进阶版] noip2013火柴排队

//火柴 
#include<bits/stdc++.h>
const int N = 200005;
const int mod = 99999997;
struct node{
    int x,p;
};
node a[N],b[N];
int n,c[N],f[N],ans;
int cmp(node a,node b){ return a.x < b.x;}

void m_sort(int l,int r){
    if(l == r) return ;
    int mid = (l + r)/ 2;  //这个地方不可以改哦 
    int i = l, j = mid + 1,k = l;
    m_sort(l,mid);
    m_sort(mid+1,r);
    while(i <= mid && j <= r){
        if(c[i] <= c[j]) f[k++] = c[i++];
        else {
            f[k++] = c[j++];
            ans += mid - i + 1;
            ans %= mod;
        }
    }
    while(i <= mid) f[k++] = c[i++];
    while(j <= r ) f[k++] = c[j++];
    for(int i = l; i <= r; i++) c[i] = f[i];
}
signed main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) {
      scanf("%d",&a[i].x);
       a[i].p =i;    
    }
    for(int i = 1; i <= n; i++) {
      scanf("%d",&b[i].x);
      b[i].p =i;    
    }
    std::sort(a+1,a+n+1,cmp);
    std::sort(b+1,b+n+1,cmp);
    for(int i = 1; i <= n; i++) c[a[i].p] = b[i].p;
    m_sort(1,n); 
    printf("%d",ans);
    return 0;
}
火柴排队

 

 

 

 

2.只涉及相邻元素的排序

这种情况下快排会浪费很多时间,而归并正是解决这种问题的利器。

e.g. NOIP2011瑞士轮

#include<iostream> 
#include<algorithm>    
using namespace std; 
#define fr(i,n)  for(register int i = 1; i <= n; ++i) 
int n,r,q;  
const int N = 200500;
int a[N],win[N],lose[N],c[N],w[N];   
int tot;
bool cmp(int x,int y)  {  
  if(c[x]==c[y])   return x<y;
  return c[x]>c[y];
}  
void merge(){
    int l = 1, r = 1, k = 0;
    while(l <= tot && r <= tot) {
        if(cmp(win[l],lose[r]))  a[++k] = win[l++];
        else a[++k] = lose[r++];
    }
    while(l <= tot) a[++k] = win[l++];
    while(r <= tot) a[++k] = lose[r++];
}
int main(){
    scanf("%d%d%d",&n,&r,&q);
    n = n * 2;
    fr(i,n) a[i] = i;
    fr(i,n) scanf("%d",c+i);
    fr(i,n) scanf("%d",w+i);
    sort(a+1,a+1+n,cmp);  //a[i]记录的是排名为i的选手是哪位 
    while(r--){
        tot = 0;
        for(register int i = 1; i <= n; i+=2){ //跳二是排名紧靠着的两位选手一起比赛 
            if(w[a[i]] > w[a[i+1]]){
              win[++tot] = a[i];   //这两行   记录了赢和输这一场的分别是哪个选手 
              lose[tot] = a[i+1];
              c[a[i]]++;    //分数++ 
            }
            else{
              win[++tot] = a[i+1];
              lose[tot] = a[i];
              c[a[i+1]]++;    
            }
        }
        merge();
    }
    printf("%d\n",a[q]);
    return 0;
}
View Code

 

#include<iostream> 
#include<algorithm>    
using namespace std; 
#define fr(i,n)  for(register int i = 1; i <= n; ++i) 
int n,r,q;  
const int N = 200500;
int a[N],win[N],lose[N],c[N],w[N];   
int tot;
bool cmp(int x,int y)  {  
  if(c[x]==c[y])   return x<y;
  return c[x]>c[y];
}  
void merge(){
    int l = 1, r = 1, k = 0;
    while(l <= tot && r <= tot) {
        if(cmp(win[l],lose[r]))  a[++k] = win[l++];
        else a[++k] = lose[r++];
    }
    while(l <= tot) a[++k] = win[l++];
    while(r <= tot) a[++k] = lose[r++];
}
int main(){
    scanf("%d%d%d",&n,&r,&q);
    n = n * 2;
    fr(i,n) a[i] = i;
    fr(i,n) scanf("%d",c+i);
    fr(i,n) scanf("%d",w+i);
    sort(a+1,a+1+n,cmp);
    while(r--){
        tot = 0;
        for(register int i = 1; i <= n; i+=2){
            if(w[a[i]] > w[a[i+1]]){
              win[++tot] = a[i];
              lose[tot] = a[i+1];
              c[a[i]]++;    
            }
            else{
              win[++tot] = a[i+1];
              lose[tot] = a[i];
              c[a[i+1]]++;    
            }
        }
        merge();
    }
    printf("%d\n",a[q]);
    retur
posted @ 2019-08-24 20:53  QUEKI嶺冬  阅读(187)  评论(0编辑  收藏  举报
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */ .pln{color:#4d4d4c}ol.linenums{margin-top:0;margin-bottom:0;color:#8e908c}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#fff;list-style-type:decimal!important;}@media screen{.str{color:#718c00}.kwd{color:#8959a8}.com{color:#8e908c}.typ{color:#4271ae}.lit{color:#f5871f}.pun{color:#4d4d4c}.opn{color:#4d4d4c}.clo{color:#4d4d4c}.tag{color:#c82829}.atn{color:#f5871f}.atv{color:#3e999f}.dec{color:#f5871f}.var{color:#c82829}.fun{color:#4271ae}} /*下面是我设置背景色,字体大小和字体*/ .cnblogs-markdown code{ background:#fff!important; } .cnblogs_code,.cnblogs_code span,.cnblogs-markdown .hljs{ font-size:16px!important; } .syntaxhighlighter a, .syntaxhighlighter div, .syntaxhighlighter code, .syntaxhighlighter table, .syntaxhighlighter table td, .syntaxhighlighter table tr, .syntaxhighlighter table tbody, .syntaxhighlighter table thead, .syntaxhighlighter table caption, .syntaxhighlighter textarea { font-size: 16px!important; } .cnblogs_code, .cnblogs_code span, .cnblogs-markdown .hljs{ font-family:consolas, "Source Code Pro", monaco, monospace !important; } //以上是代码高亮 /* 文字特效 */