[AcWing]快速排序,归并排序,整数与浮点数的二分

快速排序,归并排序,整数与浮点数的二分

问题来源 : ACWing
https://www.acwing.com/blog/content/277/

快速排序

快速排序的思想就是,每一次的处理,都会把整个数组分成两部分,选择一个数,这个数左边的部分都是比自己小的,右边的部分都是比自己大的。然后在递归处理左右两个子区间,对子区间再进行一次快速排序,然后分治下去,最后趋于有序。
在这里插入图片描述

  1. 升序排列
#include <iostream>

using namespace std;

const int N = 1e5 + 100;
int n;
int f[N];

void quit_sort(const int le,const int ri) {
    if(le >= ri) return ;
    
    // 三数取中
    int key = f[le + ri >> 1], l = le - 1, r = ri + 1;
    while(l < r) { 
        do r--; while (f[r] > key);
        do l++; while (f[l] < key);
       
        if(l < r) swap(f[l],f[r]);
    }
    
    /*
    swap(f[le],f[le + ri >> 1]);
    int key = f[le], r = le;
    for(int i = le + 1; i <= ri; i++)
        if(f[i] < key)  swap(f[++r],f[i]);
    swap(f[r],f[le]);
    quit_sort(le,r - 1); 
    quit_sort(r + 1,ri);
    */
    
    quit_sort(le,r); 
    quit_sort(r + 1,ri);
}

int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    quit_sort(0,n-1);
    
    for(int i = 0; i < n; i++) printf("%d ",f[i]);
    return  0;
}
  1. Top K

快速排序的一个应用,要求我们得到第K大或者第K小的一个数,第一反应就是对整体进行一次排序,那么最后直接返回该位置的下标就可以了。

但是,我们最后只想得到第K个数,那么从K+1,到后面的所有数据,我们对他们额外做了一次排列,这是有些浪费的。可以在快排的基础上,每次只对前面的区间进行排序,让最后得到的有序数组尽可能的在[0,k]上是有序的

#include <iostream>

using namespace std;

const int N = 1e5 + 100;
int n,k;
int f[N];

void quit_sort(const int le,const int ri) {
    if(le >= ri || le > k) return ;
    
    int x = f[le + ri >> 1], i = le - 1, j = ri + 1;
    while(i < j) {
        do i++; while(f[i] < x);
        do j--; while(f[j] > x);
        if(i < j) swap(f[i],f[j]);
    }
    
    quit_sort(le,j);
    quit_sort(j+1,ri);
}

int main() {
    scanf("%d %d",&n,&k);
    
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    quit_sort(0,n-1);
    
    printf("%d",f[k-1]);
    return 0;
}

归并排序

  1. 升序排列

也是使用了分治的一种思想,快速排序是每次从中间划分两个子区间(不一定等长)。但是归并排序采用的是分治的思想,每个区间的大小从2,4,6....直到整个区间的大小,每一次的排序都可以看做是合并两个有序数组,最后完成整个排序。

#include <iostream>

using namespace std;

const int N = 1e5 + 100;
int n;
int f[N],t[N];

void merge_sort(const int le,const int ri) {
    if(le >= ri) return ;
    
    int mid = (le + ri) >> 1;
    merge_sort(le,mid);
    merge_sort(mid + 1,ri);
    if(f[mid] <= f[mid + 1]) return ;
    
    // merge [le,ri]
    int idx = le, i = le, j = mid + 1;
    while(i <= mid || j <= ri) {
        if(i > mid) t[idx++] = f[j++];
        else if(j > ri) t[idx++] = f[i++];
        else if(f[i] <= f[j]) t[idx++] = f[i++];
        else if(f[i] > f[j])  t[idx++] = f[j++];
    }
    
    for(int i = le; i <= ri; i++) f[i] = t[i];
}

int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    merge_sort(0,n-1);
    
    for(int i = 0; i < n; i++) printf("%d ",f[i]);
    return 0;
}
  1. 逆序对的数量
#include <iostream>

using namespace std;

const int N = 1e5 + 100;
int f[N],t[N];
int n;

long long ans = 0;

void merge_sort(int le,int ri) {
    if(le >= ri) return ;
    int mid = (le + ri) >> 1;
    merge_sort(le,mid);
    merge_sort(mid+1,ri);
    
    int l = le, r = mid + 1;
    
    for(int i = le; i <= ri; i++) {
        if(l > mid) {
            t[i] = f[r++];
        } else if(r > ri) {
            ans += ri - r + 1;
            t[i] = f[l++];
        } else if(f[l] > f[r]) {
            ans += ri - r + 1;
            t[i] = f[l++];
        } else if(f[l] <= f[r]) {
            t[i] = f[r++];
        }
    }
    
    for(int i = le; i <= ri; i++) f[i] = t[i];
}

int main() {
    scanf("%d",&n);
    
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    merge_sort(0,n-1);
    
    printf("%d",ans);
    return 0;
}

二分

一个题目,如果一个区间具有单调性质,那么一定可以二分,但是如果说这道题目没有单调性质,而是具有某种区间性质的话,我们同样可以使用二分

在二分查找中,每一次我们都需要确定我们的结果所在的区间,然后将原本的区间缩减到这个区间范围中,排除无用的答案,那么最后等到不可再分的时候,那个结果就是我们需要的结果(算法正确的前提)

  1. 整数二分,数的范围

整体来说,就是找一个数第一次出现的位置,和最后一次出现的位置。
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 1e5 + 100;
int f[N];
int n,m;
int le,ri;

int main() {
    scanf("%d %d",&n,&m);
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    int x;
    while(m --) {
        scanf("%d",&x);
        int l = 0, r = n-1;
        int a,b;
        
        while(l < r) {
            int mid = (l + r) / 2;
            if(f[mid] >= x)  r = mid;
            else if(f[mid] < x) l = mid + 1;
        }
        if(f[l] != x) {
            printf("-1 -1\n");
            continue;
        }
        a = l;
        
        l = 0, r = n - 1;
        while( l < r ) {
            int mid = (l + r + 1) / 2;
            if(f[mid] <= x) l = mid;
            else if(f[mid] > x) r = mid - 1;
        }
        
        printf("%d %d\n",a,l);
    }
    return 0;
}
  1. 数的三次方根
#include <iostream>

using namespace std;

double n;

int main() {
    scanf("%lf",&n);
    
    double l = -10000.0, r = 100000.0;
    while(r - l > 1e-8) {
        double mid = (l + r) / 2;
        if(mid * mid * mid <= n) l = mid;
        else r = mid;
    }
    
    printf("%.6lf\n",l);
    return 0;    
}
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页