算法基础课(一)——排序与二分

2020-01-30   


快速排序

# include <iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N];
void qsort(int a[], int l, int r){// 通过
    if(l>=r)return ;
    int x = a[l+r>>1], i=l-1, j=r+1;
    while(i<j){
        while(a[++i]<x);
        while(a[--j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    qsort(a, l, j);qsort(a,j+1,r);
}
void quick_sort(int a[], int l, int r){
    if (l >= r) return;
    int x = a[l+r>>1], i = l-1, j = r + 1;
    while(i < j){
        do i ++; while(a[i] < x);
        do j --; while(a[j] > x);
        if(i < j) swap(a[i], a[j]);
    }
    quick_sort(a, l, j);//
    quick_sort(a, j+1, r);
}
void quick_sort(int a[], int l, int r){
    if (l >= r) return;
    int x = a[l+r+1>>1], i = l-1, j = r + 1;//边界不能取 l, 如[1,2]会死循环
    while(i < j){
        do i ++; while(a[i] < x);
        do j --; while(a[j] > x);
        if(i < j) swap(a[i], a[j]);
    }
    quick_sort(a, l, i - 1);//用i枢纽则必须不能取到左边界;j,右。
    quick_sort(a, i, r);
}
void quick_sort(int a[], int l,int r)//快速排序
{// 这个代码没办法对有很多重复元素的数组降低复杂度
	if(l>=r)return;
	swap(a[l], a[l+r>>1]);// 枢纽可以随机一个
	int base=a[l];//base的作用是进行划分后,使得所有在base左边的元素小于等于base,
	//所有在base右边的元素大于等于base 
	int i=l,j=r;
	while(i<j) // 指针 j 必须先动
	{
		while(i<j&&a[j]>=base) j--; //找到右边小于base的元素下标 
		
		while(i<j&&a[i]<=base) i++; //找到左边大于base的元素下标 
		
		if(i < j) swap(a[i],a[j]);//a[i]>base and a[j]<base,左右替换,使得a[i]<=base and a[j]>=base 
	}
	a[l]=a[i];//最后的替换 
	a[i]=base;
	// for(int i=0;i<n;i++) cout<<a[i]<<" "; cout << endl;
	quick_sort(a, l,i-1);
	quick_sort(a, i+1,r);
}
int main(){
    int n; cin>>n;
    for(int i=0; i< n;i++) cin>>a[i];
    quick_sort(a, 0, n-1);
    for(int i=0;i<n;i++)cout<<a[i]<<" ";
}

第k大的数

int quick_select(int l, int r, int k){
    if(l==r) return a[l];
    int x=a[l], i=l-1, j=r+1;
    while(i<j){
        while(a[++i]<x);
        while(a[--j]>x);
        if(i<j)swap(a[i], a[j]);
    }
    int sl = j-l+1; // 比当前枢纽a[j]小的元素的个数
    if(k <= sl) return quick_select(l, j, k);
    return quick_select(j+1, r, k-sl);//去掉比我小的sl个元素,变成第k-sl大
}

归并排序

#include<iostream>
using namespace std;
const int N = 100010;
int a[N], n;
void merge_sort(int a[], int l, int r){
    if (l >= r) return;
    int mid = l + r >> 1;
    merge_sort(a, l, mid), merge_sort(a, mid + 1, r);
    int i = l, j = mid + 1, w[N], k = 0;
    while(i <= mid && j <= r) {
        if (a[i] <= a[j]) w[k++] = a[i++];
        else w[k++] = a[j++];
    }
    while(i <= mid) w[k++] = a[i++];
    while(j <= r) w[k++] = a[j++];
    for(int k = 0, i = l; i <= r;) a[i++] = w[k++];
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    merge_sort(a, 0, n-1);
    for(int i=0;i<n;i++) cout<<a[i]<< " ";
}

逆序对的个数

#include <iostream>
using namespace std;

const int N = 100010;
int n, a[N];

typedef long long ll;

ll merge_sort(int l, int r){
    if(l >= r) return 0;
    int mid = l + r >> 1;
    ll res = merge_sort(l, mid) + merge_sort(mid+1, r);
    int i = l, j = mid + 1, w[N], k = 0;
    while(i <= mid && j <= r) {
        if (a[i] <= a[j]) w[k++] = a[i++];
        else {
            w[k++] = a[j++];
            res += mid - i + 1;
        }
    }
    while (i <= mid) w[k++] = a[i++];
    while (j <= r) w[k++] = a[j++];
    for(int k = 0, i = l; i <= r;) a[i++] = w[k++];
    return res;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    
    cout<<merge_sort(0, n-1)<<endl;
}

整数二分

有单调性可二分,没单调性的有时也可二分。二分的本质是边界,区间可被分为满足某性质和不满足某性质的两部分。

// 区间 [l, r] 被划分为[l, mid]和[mid+1, r]时使用。
int bsearch_1(int l, int r){
    while(l < r){
        int mid = l + r >> 1;
        if (check(mid)) r = mid; // 判定 mid 是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间 [l, r] 被划分为[l, mid-1]和[mid, r]时使用。
int bsearch_2(int l, int r){
    while(l < r){
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid; // 判定 mid 是否满足性质
        else r = mid - 1;
    }
    return l;
}

Acwing:789 数的范围

# include<iostream>
using namespace std;

const int N = 100010;
int a[N];
int n, q;

int left(int a[], int x){
    int l = 0, r = n-1;
    while(l < r){
        int mid = l + r >> 1;
        if(a[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return a[l] == x ? l:-1;
}
int right(int a[], int x){
    int l = 0, r = n - 1;
    while(l < r){
        int mid = l + r + 1 >> 1;
        if(a[mid] <= x) l = mid;
        else r = mid - 1;
    }
    return a[l] == x ? l: -1;
}
int main(){
    cin >> n >> q;
    for(int i = 0; i < n; i ++) cin >> a[i];
    while(q--){
        int k; cin >> k;
        int l = left(a, k), r = right(a, k);
        cout << l << " " << r << endl;
    } 
}

浮点数二分

double bsearch(double x){
    // 求平方根
    double l = 0, r = x;
    while(r - l > 1e-8){ // 或者直接迭代 100 次
        double mid = (l + r) / 2;
        if(mid * mid >= x) r = mid;
        else l = mid;
    }
    return l;
}

Q.E.D.


我是星,利剑开刃寒光锋芒的银星,绝不消隐