一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。

1.快速排序

算法思想:选择基准元素,比基准元素小的放左边,比基准元素大的放右边。每趟至少一个元素排好。

每一趟实现步骤:

  • low>=high,返回,排序完成
  • 选取基准元素x=a[low],i=low,j=high
  • 当i
  • 从右至左找到第一个小于x的元素,移到a[i]的位置
  • 从左至右找到第一个大于x的元素,移到a[j]的位置,转3

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第1张图片一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第2张图片

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第3张图片一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第4张图片

参考:最详细的排序算法——快速排序 - 知乎 

  • 循环结束,i=j,将a[i]=x,此时左边的元素小于x,右边的元素大于x,x排好
  • 对x左边进行递归排序
  • 对x右边进行递归排序

代码实现:

#include 
using namespace std;

const int N = 100010;

int n;
int q[N];

void quick_sort(int a[], int low, int high){

    if (low >= high) return;
+
    int x = a[low],i=low,j=high;
    while(i < j){                       // 边界条件
        while(a[j]>x && i

应用

给定一个长度为 n的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k个数。

分析:题目只要求找到第k个数,直接排序会用力过猛,时间复杂度更高。

算法思想:采用快排的思想,取基准元素x,进行一趟快速排序

  • 若x的位置i小于k,则应该在右侧寻找

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第5张图片

  • 若x的位置i大于k,则应该在左侧寻找 

 一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第6张图片

  • 若i的位置等于k,则找到该元素,返回 

代码实现:

#include 
using namespace std;

const int N = 100010;

int n;
int q[N];

int quick_sortk(int a[], int low, int high, int k){

    int x = a[low],i=low,j=high;
    while(i < j){                   
        while(a[j]>x && i> n >>k;
    for(int i=0;i

2.归并排序

算法分析:我们知道直接插入排序在元素有序时,时间复杂度可以达到O(N),归并排序则是借助于顺序,从1个元素开始归并,让每一段接近于有序,然后实行有序表的归并。

实现步骤:

  • 递归终止条件,l>=r,则返回
  • 从中间分开,递归归并左侧于右侧,递归执行时,会压入栈中,直到归并段为1
  • 进行有序表的归并,利用双指针不回溯,谁小谁尾差进辅助数组中,然后后移。

代码实现:

#include 

const int N = 100010;
int q[N];
void merge_sort(int a[],int l, int r){

    if(l>=r) return;

    int mid = (l+r)/2;       //从中间分开
    merge_sort(a,l,mid);     // 递归归并左侧
    merge_sort(a,mid+1,r);   // 递归归并右侧

    int length = r-l+1;      // 辅助数组长度
    int temp[length];        // 开辟辅助数组
    int i=l,j=mid+1,k=0;
    while(i<=mid && j<=r){   // 双指针不回溯合并有序段
        if (q[i]<= q[j])
            temp[k++] = q[i++];
        else
            temp[k++] = q[j++];
    }

    while(i<=mid) temp[k++] = q[i++];  // 将剩余部分插入有序链表中
    while(j<=r) temp[k++] = q[j++];

    for(i=0;i< length;i++) q[l+i] = temp[i];  // 将有序辅助数组的内容存放回原数组中

}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i
应用: 逆序对的数量

给定一个长度为 n的整数数列,计算数列中的逆序对的数量。

算法思想:

1.采用分治的思想解决问题,对于每一次分段,逆序对有三种类型,即左侧逆序对的数量、右侧逆序对的数量、左右两侧逆序对的数量。左侧逆序对的数量和右侧逆序对的数量均可以通过分治转化为求左右两侧逆序对的数量

2. 如何求解左右两侧逆序对的数量呢?在进行归并合并时。如果a[i]>a[j],那么a[j]左侧同一段内的所有元素均能与a[i]组成一对逆序对。

代码实现:

#include 
using namespace std;

const int N = 100010;
int q[N];

void reverse_order(int a[],int l, int r, int* number){

    if(l>=r) return;

    int mid = (l+r)/2;       //从中间分开
    reverse_order(a,l,mid,number);     // 递归归并左侧
    reverse_order(a,mid+1,r,number);   // 递归归并右侧

    int length = r-l+1;      // 辅助数组长度
    int temp[length];        // 开辟辅助数组
    int i=l,j=mid+1,k=0;
    while(i<=mid && j<=r){   // 双指针不回溯合并有序段
        if (a[i]<= a[j])
            temp[k++] = a[i++];
        else{
            temp[k++] = a[j++];*number+=mid-i+1; // 如果a[i]>a[j],那么a[j]左侧同一段内的所有元素均能与a[i]组成一对逆序对。
        }
    }

    while(i<=mid) temp[k++] = a[i++];  // 将剩余部分插入有序链表中
    while(j<=r) temp[k++] = a[j++];

    for(i=0;i< length;i++) a[l+i] = temp[i];  // 将有序辅助数组的内容存放回原数组中

}

int main()
{
    int n;
    int number = 0;
    scanf("%d",&n);
    for(int i=0;i

3.二分

(1)整数二分

给定一个按照升序排列的长度为 n的整数数组,以及 q个查询。对于每个查询,返回一个元素 k的起始位置和终止位置(位置从 0,0开始计数)。如果数组中不存在该元素,则返回 -1 -1。

算法分析:此处的二分是二分查找的拓展,要求能够找到与查询key相等的所有元素的起始位置和终止位置。

算法思想:

1.寻找左边界: 

  • mid=(low+high),即向下取整。
  • 当a[mid]
  • 当a[mid]>=key时,让high=mid,由于向下取整,此时high会逐渐的靠近low,可将low=high作为循环终止条件。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第7张图片

2.寻找右边界

  • mid=(low+high+1)/2,即向上取整
  • 当a[mid]>key,时high=mid-1;最终,high指向相等元素的终止位置。
  • 当a[mid]<=key时,low=mid.此时由于向上取整,low会逐渐靠近high,可将low=high作为循环终止条件。

代码实现: 

#include 
using namespace std;

const int N = 100010;
int q[N];

int bin_search_l(int a[], int low, int high, int k){ // 找左边界

    if(low>=high) return low;  // low=high时,返回左边界
    int mid = (low+high)/2;
    if(a[mid]=high) return low;  // low=high时,返回右边界
    int mid = (low+high+1)/2;   // +1是为了防止死循环,这里要求左边界趋近于右边界,需要向上取整
    if(a[mid]<= k) return bin_search_r(a,mid, high,k); // // 由于寻找的是右边界,要求左边界趋近于右边界,即l=mid,条件包含a[mid]=k
    else return bin_search_r(a,low,mid-1,k);// 寻找右边界
}


int main()
{
    int n;         // n为数组长度
    int m;         // m为查询元素个数
    int l,r;
    int key;
    cin >> n >> m;
    for(int i=0;i

(2)浮点数二分

给定一个浮点数 n,求它的三次方根。

浮点数二分的循环终止条件为high-low<1e-8,此时不用太过考虑终止条件。

#include 
#include 

using namespace std;

int main(){

    double m;
    cin >> m;
     double l,r;
    if(m<0){       
        l=m;r=0;  // m <-1,立方根在区间[m,0]
        if(m>-1) l = -1; //-11,立方根在区间[0,m]
        if(m<1) r = 1;  // 01e-8){
        mid = (l+r)/2;
        if(pow(mid,3)

 4.高精度

高精度大整数之间的运算,此时由于数字较大,可能超出了计算机的表示范围,也可能出现溢出,使用数组存储每一个大整数。

(1)高精度加法

与我们的加法计算相似,每一位的计算结果t为对应位的数字、低位进位之和。t%10为该位数字,t/10为进位。

 一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第8张图片

#include 
#include 
using namespace std;

vector h_add(vector &A,vector &B){
    vector C;
    int t=0;
    int n;
    for(int i=0;i A,B;  // 在高精度中,使用数组存储一个比较大的数的每一位
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); // 涉及到进位,低位存储在数组左侧,高位存储在右侧
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    auto C = h_add(A,B);
    for(int i = C.size()-1;i>=0;i--) printf("%d",C[i]);
    return 0;

}

(2) 高精度减法

与我们的减法运算原理相同,每一位的减法结果为对应位数字减法之差,同时考虑低位借位。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第9张图片

#include 
#include 
using namespace std;


bool comp(vector &A,vector &B){  // 比较A,B两个数的大小
    if(A.size()!= B.size()) return A.size() > B.size(); // 先看位数,位数多的数数值大
    for(int i = A.size()-1;i>=0;i--)
        if(A[i]!=B[i]) return A[i]>B[i];  // 从高位到低位依次比较,高位数值越大,数越大
    return true;
}

vector h_sub(vector &A,vector &B){
    vector C;
    int t=0;
    int n;
    for(int i=0;i1 && C.back()==0) C.pop_back();  // 删除高位多余的0
    return C;
}

int main(){
    string a,b;
    vector A,B;
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    if(comp(A,B)){     // A>B,返回A-B
        auto C = h_sub(A,B);
        for(int i = C.size()-1;i>=0;i--) printf("%d",C[i]);
    }
    else{             // A=0;i--) printf("%d",C[i]);
    }

    return 0;

}

(3)高精度乘法

根据计算机组成原理我们知道,能够通过加法和移位实现乘法运算。 即:用一个数的每一位乘以另一个数的每一位,然后移位相加。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第10张图片

#include 
#include 
using namespace std;

vector h_mul(vector &A,vector &B){ //使用加法和移位实现高精度乘法
    vector C(A.size()+B.size(),0); // 两个n位数相乘,最多2n位
    int n,t;
    for(int i =0;i1 && C.back()==0) C.pop_back();
    return C;
}

int main(){
    string a,b;
    vector A,B;
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    auto C = h_mul(A,B);
    for(int i = C.size()-1;i>=0;i--) printf("%d",C[i]);
    return 0;

}

(4)高精度除法

类似于我们计算除法的方式,从最高位开始,依次下移移位数字,求商和余数。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第11张图片

#include 
#include 
#include 
using namespace std;

vector h_div(vector &A,int B, int &r){   // 一个高精度大整数除以一个小整数
    vector C;
    r = 0;
    int n;
    for(int i=A.size()-1;i>=0;i--){
        r = r *10 + A[i];       // 将下一位数字移下来,计算
        n = r / B;              // 该位的商
        C.push_back(n);
        r = r % B;             // 余数
    }
    reverse(C.begin(), C.end());   // 在数组C中,商的高位在左侧,低位在右侧,逆置数组
    while(C.size()>1 && C.back()==0) C.pop_back(); // 去除高位多余的0
    return C;
}

int main(){
    string a;
    int b;
    vector A;
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
    int r;
    auto C = h_div(A,b,r);
    for(int i = C.size()-1;i>=0;i--) printf("%d",C[i]);
    printf("\n%d",r);
    return 0;

}

5.差分与前缀和

差分与前缀和是一对逆运算,都是以空间换时间的思想。

前缀和矩阵即存储另一个矩阵的前N项和,即

       a1  a2  a3  a4   a5   a6  a7……
s0    s1  s2  s3   s4   s5    s6  s7……
其中,s0=0,s1=a1,s2=a1+a2,s3=a1+a2+a3……,s[n]为a[n]的前缀和
          a1=s1-s0 ,a2=s2-s1, a3=s3-s2 …… ,a[n]为s[n]的差分矩阵

(1)前缀和

前缀和即通过构建前缀和数组,从而实现在O(1)的时间复杂度内求出[L,R]区间的和,其结果为S[R]-S[L-1]

实例:输入一个长度为 n 的整数序列。接下来再输入 m个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l个数到第 r 个数的和。

#include 
using namespace std;

const int N = 10010;

int main(){
    int m,n;       // m个询问,数组长度为n
    int a[N],s[N]; // a[n]为原数组,s[n]为前缀和
    s[0] = 0;
    scanf("%d%d",&n,&m);
    for(int i=0;i> l >> r;   // 子区间的和
        printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
}

(2)二维矩阵前缀和

输入一个 n行 m列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。

此时,前缀和矩阵存储的结果是a[i][j]与a[1][1]子矩阵的和。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第12张图片

 对于前缀和矩阵的构建,s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1]

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第13张图片

对于任意子矩阵的和=s[x2][y2]-s[x1-1][y1]-s[x1][y1-1]+s[x1-1][y1-1]。需要注意的是,下图中只有网格点处才表示值

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第14张图片

输入一个 n行 m列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

#include 
using namespace std;

const int N = 1010;
int a[N][N],s[N][N];

int main(){
    int m,n,q;
    cin >> n >> m >> q;
    //scanf("%d%d%d",&n,&m,&q);
    for(int i=0;i> x1 >> y1 >> x2 >> y2;
        printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
    }
    return 0;
}

(3)差分

差分的作用是将[L,R]区间同时加c与同时减c的时间复杂度降为0(1)。即相当于让差分矩阵b[l]+c;b[r+1]-c;也就是所,在O(1)的时间复杂度内保存一次更新,所有更新累计起来,最后再用 O(N)的时间复杂度整体更新。

一维差分

输入一个长度为 n 的整数序列。接下来输入 m个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r]之间的每个数加上 c。请你输出进行完所有操作后的序列。

#include 
using namespace std;

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

void insertm(int l, int r,int c, int b[]){

   b[l] += c;
   b[r+1] -= c;

}

int main(){
    int m,n;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) insertm(i,i,a[i],b);// 初始化时,矩阵相当于对[L,L]区间加上a[i]
    int l,r,c;
    while(m--){   // m次更新
        cin >> l >> r >> c;
        insertm(l,r,c,b);
    }
    for(int i=1;i<=n;i++) {
        a[i]=a[i-1]+b[i];
        printf("%d ",a[i]);

    }
}

(4)二维差分 

二维矩阵在x1,y1,x2,y2区间内同时加c相当于对于差分矩阵b[x1][y1] += c; b[x2+1][y1] -= c; b[x1][y2+1] -= c;明显发现重叠区域多减了c,加上b[x2+1][y2+1] += c;

 一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第15张图片

图参考:找不到页面 - AcWing 

输入一个 n行 m列的整数矩阵,再输入 q个操作,每个操作包含五个整数 x1,y1,x2,y2,c其中 (x1,y1) 和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上 c。

请你将进行完所有操作后的矩阵输出。

#include 
using namespace std;

const int N = 1010;
int a[N][N],b[N][N];

void insertM(int x1, int y1, int x2, int y2, int c,int b[][N]){
    b[x1][y1] += c;
    b[x2+1][y1] -= c;
    b[x1][y2+1] -= c;
    b[x2+1][y2+1] += c;
}

int main(){
    int m,n,q;//  n行m列的整数矩阵,再输入q个操作
    cin >> n >> m >> q;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
            insertM(i,j,i,j,a[i][j],b); // 初始化,相当于在ijij区间加上a[i][j]
        }
    }
    int x1,y1,x2,y2,c;
    while(q--){   // 指定区间+c
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insertM(x1,y1,x2,y2,c,b);
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j]; // 前缀和公式
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }

}

6. 双指针

给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

算法思想:采用双指针策略i,j;i,j指向不重复子序列的开始和结束,辅助数据b[n]记录i,j区间内元素出现的次数。i前移,如果出现了重复元素,则j前移,直到区间内消除重复元素。 

#include 
using namespace std;

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

int main(){
    int n;     // 长度为n的数组
    cin >> n;
    int j = 0,res=0;  // res记录要求序列的长度
    for(int i=0;i1){  // 此时,说明a[i-1]与a[i]重复,让j前移直到序列中无重复元素
            b[a[j]]-=1;
            j++;
        }
        res = max(res,i-j+1); // 更新序列长度
    }
    printf("%d",res);
    return 0;
}

给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。数组下标从 0 开始。请你求出满足 A[i]+B[j]=x 的数对 (i,j)。数据保证有唯一解。

算法思想:寻找A[i]+B[j]=x的数对,即找一个较小的数与一个较大的数的和,使其等于x。

因此,可采用双指针,i从0开始,j从n开始,按如下步骤进行:

首先固定a[i],让j前移,如果出现了a[i]+b[j]

固定a[j],让i后移,如果出现了a[i]+b[j]>x,则不可能找到与a[j]搭配的数,j前移

即:

#include 
using namespace std;

const int N = 100010;

int a[N],b[N];

int main(){
    int n,m,x;
    cin >> n >> m >> x;
    for(int i=0;i=0){
        if(a[i]+b[j]==x) {  // 找到数对
            printf("%d %d",i,j);
            break;}
        else if(a[i]+b[j] < x) i++; // 如果出现了a[i]+b[j]x,则不可能找到与a[j]搭配的数,j前移
    }
    return 0;
}

给定一个长度为 n的整数序列 a1,a2,…,an以及一个长度为 m 的整数序列 b1,b2,…,bm。

请你判断 a序列是否为 b序列的子序列。

子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5}是列 {a1,a2,a3,a4,a5}的一个子序列。

算法思想:该题没有要求连续的公共子序列,因此,只需要从左向右遍历a数组,判断a数组的每一个元素是否在b数组中,且保持其相对顺序。

因此:

#include 
using namespace std;

const int N = 100010;

int a[N],b[N];

int main(){
    int n,m;
    cin >> n >> m;
    for(int i=0;i

7.位运算

让我们回到计算机组成原理的求补电路,已知[x]补,[-x]补为将[x]补最低位的0和第一位1保留,其余各位包括符号位取反。即:

[x]补=01111010

[-x]补=10000110

此时,若将x与-x,则结果为00000010,即得到x最低位的1。

应用: 

给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 11 的个数。

算法思想:每次让x减去最低位的1,然后统计 

#include 
using namespace std;

const int N = 100010;

int lowbit(int x){
    return x & -x;  // 取x中最低位的1
}

int main(){
    int n,x,res;
    cin >> n;
    while(n--){
        scanf("%d",&x);
        res = 0;
        while(x) {
            x-=lowbit(x); // x每次减去最低位的1,然后统计1的个数
            res++;
        }
        cout << res <<" ";
    }
    cout << endl;
    return 0;
}

8.区间和

示例:假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。接下来,进行 m次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r]之间的所有数的和。

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第16张图片

 算法思想:快速求区间和,很明显可以构建前缀和数组快速求解,但是在这个场景中,数组稀疏且无限长。因此,我们可以将区间离散化,只存储需要进行处理的位置,从而降低空间复杂度。具体包括以下步骤:

  • 首先,存储位置x和区间l,r于同一数组中
  • 进行去重并且排序
  • 将位置从小到大映射为1,2,3,……
#include 
#include 
#include 

using namespace std;
typedef pair PII;

const int N = 300010; // n+2*m,代表操作完成后非0元素的个数

int a[N],s[N];

int find(int x, vector &all){
    int l=0,r=all.size()-1;
    int mid;               // 二分法查找元素
    while(l> n >> m;
    vector add;   // 存储加操作
    vector query; // 存储区间l,r
    vector all;   // 对位置x,l,r进行映射
    int x,c,l,r;
    while(n--){
        cin >> x >> c;
        add.push_back({x,c}); 
        all.push_back(x); 
    }
    while(m--){
        cin >> l >> r;
        query.push_back({l,r});
        all.push_back(l);
        all.push_back(r);
    }
    sort(all.begin(),all.end()); // 排序
    all.erase(unique(all.begin(),all.end()),all.end()); // 去重,unique将重复元素放在数组末尾,并返回重复元素的起始位置
    for(auto item : add){
        x = find(item.first,all); // 位置x映射为相应下标
        a[x] += item.second;   // 操作数
    }
    for(int i=1;i<=all.size();i++) s[i] = s[i-1] + a[i]; // 求前缀和矩阵
    for(auto item : query){
        l = find(item.first,all);   // 区间l,r进行映射
        r = find(item.second,all);
        printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
}

9.区间合并

使用贪心策略进行区间合并,其步骤如下:

1.首先对区间左端点进行排序,即:

一、基础算法之排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并内容。_第17张图片

2.对于前后相邻的2个区间,有两种关系,即有交集和无交集。若无交集,则当前区间即为独立的区间。若有交集,则进行合并,end=max(end,next_end)。

代码如下:

#include 
#include 
#include 

using namespace std;

typedef pair PII;

void q_merge(vector &a){
    sort(a.begin(),a.end());  // 以左端点为基准,对区间进行排序
    vector res;
    int st = -2e-9,ed = 2e-9; // 初始,区间的st和ed为负无穷
    for(auto item : a){
        if(ed < item.first){  // 当前区间和下一区间无交集.
           if(st!=-2e9) res.push_back({st,ed}); // st为负无穷代表初始化值
            st = item.first;  // 当前区间为独立区间,st,ed指向下一区间
            ed = item.second;
        }
        else{   // 当前区间与下一区间有交集,合并
            ed = max(ed,item.second);
        }
    }
    if(st!=-2e-9) res.push_back({st,ed}); // 最后一个区间进入数组
    a = res;
}

int main(){
    vector seg;
    int n,l,r;
    cin >> n;
    while(n--){
        cin >> l >> r;
        seg.push_back({l,r});
    }
    q_merge(seg);
    printf("%d",seg.size());
    return 0;
}

参考:AcWing

你可能感兴趣的:(C++与算法题系列,算法,数据结构)