第一周学习总结

本文详细介绍了快速排序、归并排序、二分查找、高精度计算、前缀和、差分数组和双指针技巧等IT技术,以及vector、lower-bound、upper-bound和pair的使用。这些算法适用于大规模数据处理和高效查找操作。
摘要由CSDN通过智能技术生成

学习目标:

  • 快速排序
  • 归并排序
  • 二分
  • 高精度
  • 前缀和
  • 差分
  • 双指针
  • 位运算
  • 离散化

学习内容:

快速排序:

  • 核心思想:分治、递归
  • 核心代码:
`void quick_sort(int q[],int l ,int r)
{
if(l>=r) return;
int x=q[l + r >> 1];
int i=l - 1,j = r + 1;

while(i < j)
{
do i++;while(q[i] < x);
do j–;while(q[j] > x);
if(i < j) swap(q[i],q[j]);
}

quick_sort(q,l,j);
quick_sort(q,j + 1,r);
}`
  • 适用范围:数据规模大时。
  • 关键点:让right先找,如果left先找相遇位置比x值大不能交换。

归并排序:

  • 核心思想:分治
  • 核心代码:
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 k=0,i=l,j=mid+1;
    while(i<=mid&&j<=r)
    {
        if(a[i]<a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(int i=l,j=0;i<=r;i++,j++) a[i]=tmp[j];
}`
  • 适用范围:数据规模大时

二分:

  • 适用范围:上下界[a, b]确定、函数在[a, b]内单调。
  • 核心代码:
        int x;
        cin>>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;
        }
        
        if(a[l]!=x)
        {
            cout<<"-1 -1"<<endl;
        }
        else
        {
        	cout << l << " ";
        l=0, r=n-1;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(a[mid]<=x) l=mid;
            else r=mid-1;
        }
        cout << l << 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;
    else l = mid + 1;
   }
    return l;
 }

区间被分为[l,mid-1]和[mid,r]时:

int bserach_2(int l,int r)
{ 
   while(l<r)
   int mid = l+r+1>>1;
   if(check(mid)) l=mid;
   else r = mid-1;
}
return l;
}
  • 查找第一个>=x的数:
    if(data[mid]>=x)
    用模版二

  • 查找最后一个<=x的数:
    if(data[mid]<=x)
    用模版一

  • 浮点数二分不需要考虑边界问题。

高精度:

  • 适用范围:数据过大时
  • 核心思想:利用字符串模拟数字计算。
  • 加法核心代码:
int t = 0;
     for (int i = 0; i < A.size() || i < B.size(); i++)
     {
         if (i < A.size()) t += A[i];
         if (i < B.size()) t += B[i];
         C.push_back(t % 10);
         t /= 10;
     }
     if (t)C.push_back(1);
  • 减法:
 bool cmp(vector<int>& A, vector<int>& 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;
 }


   for (int i = 0, t= 0; i < A.size(); i++)
     {
         t = A[i] - t; 
         if (i < B.size()) t -= B[i];
         C.push_back((t + 10) % 10);
         if (t < 0) t = 1; 
         else t = 0; 
     }
     while (C.size() > 1 && C.back() == 0) C.pop_back();
  • 乘法:
    int t=0;
    for(int i=0;i<A.size();i++)
    {
        t+=A[i]*b;
        C.push_back(t%10);
        t=t/10;
    }
    if(t) C.push_back(t);
    while(C.size()>1&&C.back()==0) C.pop_back();
  • 除法:
vector<int>C;
    r=0;
    for(int i=A.size()-1;i>=0;i--)
    {
        r=r*10+A[i];
        C.push_back(r/b);
        r=r%b;
    }
    reverse(C.begin(),C.end());
    while(C.size()>1&&C.back()==0) C.pop_back();

前缀和:

  • 适用范围:可将O(n*n)的时间复杂度简化为O(1)
  • 核心代码:
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		s[i] = s[i - 1] + a[i];
  • 二维前缀和:
    核心代码:s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]
    部分前缀和公式:s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
    在这里插入图片描述
    在这里插入图片描述

差分:

  • 一个长度为n的数组{a1,a2,…an}
    构造一个b数组,使ai=b1+b2+…bi;
    b1=a1,b2=a2-a1,bn=an-an-1
    差分与前缀和互为逆运算
  • 适用范围:给定区间[l,r]对该区间内所有数加或减c,可将时间复杂度降为O(1).
    O(n)的时间通过b数组还原出a数组
  • 核心代码:
int insert( int l, int r, int c){
    b[l] = b[l]+c;
    b[r+1] = b[r+1] -c;
}

例题
在这里插入图片描述

#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N],b[N];
int insert( int l, int r, int c){
    b[l] = b[l]+c;
    b[r+1] = b[r+1] -c;
}
 
int main(){
    
    int n, m;
    scanf("%d %d", &n, &m);
    int c;
  
    for(int i = 1; i <= n; i++){
        scanf("%d", &c);
        a[i] = c;
        insert( i, i, c); 
    }
    int l, r;
    while(m--){
        scanf("%d %d %d", &l, &r, &c);
        insert( l, r, c);
    }
    
    for(int i = 1; i <= n; i++){
        a[i] = a[i-1] + b[i]; 
    }
  
    for(int i = 1; i <= n; i++){
        printf("%d ", a[i]);
    }
 
    return 0;
}

双指针

  • 核心代码:
for (int i = 0, j = 0; i < n; i ++ ) 
{
    while (j < i && check(i, j)) j ++ ;

    // 每道题目的具体逻辑
}

核心思想:

  for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
          
        }
    }

将上面的朴素算法优化葱O(n*n)到O(n)

  • 适用范围;
    对于一个序列,用两个指针维护一段区间,比如快排的划分过程
    对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
  • 例题:
    在这里插入图片描述
#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N],s[N];
int main()
{
    int n;
    cin>>n;
    int res = 0;
    for(int i = 0; i < n; i++) cin>>a[i];
    
    for(int i = 0, j = 0; i < n; i++)
    {   
        s[a[i]]++; 
       
        while(j <= i && s[a[i]] > 1) 
        {
            s[a[j]]--; 
            j++;
        }
         res = max(res, i - j +1);
    }
    cout<<res;
    return 0;
}

位运算

  • 给定一个数字n,求n的二进制表示中第k位数字:
    先把第k位移到最后一位:n >> k(n从第0位开始)
    然后根据个位的数字来确定第k位的数字:x & 1

  • 给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数:
    核心代码:`返回n的最后一位1:lowbit(n) = n & -n

  • 例题:
    在这里插入图片描述

#include<iostream>
using namespace std;
int n,x;
int lowbit(int a){
    return x&(-x);
}
int main(){
    cin>>n;
    while(n--){
        cin>>x;
        int ans=0;
        while(x){
            x=x-lowbit(x);
            ans++;
        }
        cout<<ans<<" ";
    }
    return 0;
}

`

离散化

  • 适用范围:假设几个数1 50 60000 100000000;如果直接开这么大的数组不现实,所以适用离散化将这些间隔大的点映射到相邻的数组元素中
    a[] 1,50,600000,100000000

     ↓,↓, ↓,↓
    
     1,2,3,4,
    
  • 核心代码:

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}

  • 例题:
    在这里插入图片描述
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 3e5 + 10;  
vector<int> alls; 
vector<PII> add, query;  
int a[N], s[N];
int n, m;
int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while (l < r) {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    
    return l + 1; 
}
int main()
{
    cin >> n >> m;
    
    for (int i = 0; i < n; ++i) {
        int x, c;
        cin >> x >> c;
        
        alls.push_back(x);  
        add.push_back({x, c});  
    }
    
    for (int i = 0; i < m; ++i) {
        int l, r;
        cin >> l >> r;
        
        alls.push_back(l);  
        alls.push_back(r);  
        query.push_back({l, r});
    }
 
    sort(alls.begin(), alls.end());
  
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    
    for (auto item : add) {
        int x = find(item.first);
        a[x] += item.second;
    }
    
    for (int i = 1; i <= alls.size(); ++i) s[i] = s[i - 1] + a[i];
    
    for (auto item : query) {
        int l = find(item.first);
        int r = find(item.second);

        cout << s[r] - s[l - 1] << endl;
    }
    return 0;
}

补充:

vector:

vector详解

lower-bound、upper-bound:

lower-bound、upper-bound详解

pair:

pair C++用法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值