1.3基础算法 | 双指针算法、位运算、离散化、区间合并

1.3 基础算法(三)

这是我的一个算法网课学习记录,道阻且长,好好努力

1.3.1 双指针算法

模板

// 模板
for (i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;
    
    //每道题具体逻辑
}

// 核心思想:将朴素的算法由O(n^2)优化到O(n)

例题1:输入一个字符串,将其中的每一个单词输出出来

#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    char str[1000];

    gets(str);

    int n = strlen(str);

    for (int i = 0; i < n; i ++ )
    {
        int j = i;
        while (j < n && str[j] != ' ') j ++ ;

        // 这道题的具体逻辑
        for (int k = i; k < j; k ++ ) cout << str[k];
        cout << endl;

        i = j;
    }

    return 0;
}

// 循环次数是小于2n的,因此将时间复杂度降低了

例题2:最长连续不重复子序列

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int a[N], s[N]; // a[N] 整数序列  s[N] 表示当前j到i区间每一个数出现的次数

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> a[i]; // 读入n个数
    
    int res = 0; // 存储最大不重复字符串长度
    
    for (int i = 0, j = 0; i < n; i ++ )
    {
        s[a[i]] ++ ; // 标记某数的数量
        
        while (s[a[i]] > 1) // 判断是否重复
        {
            s[a[j]] -- ; // 如果重复,取出该数
            j ++ ; // 指针向后移动
        }
        
        res = max(res, i - j + 1); // 更新res
    }
    cout << res << endl;
    
    return 0;
}

// 注意 i指针在后面 j指针在前面

1.3.2 位运算

例题:二进制中1的个数

# include <iostream>

using namespace std;

int lowbit(int x) // 返回x的最后一位1
{
    return x & -x; // x & -x == x & (~x + 1)
}

int main()
{
    int n;
    cin >> n;
    while (n -- )
    {
        int x;
        cin >> x;

        int res = 0;
        while (x) x -= lowbit(x), res ++ ; // 每次减去x的最后一位1

        cout << res << ' ';
    }

    return 0;
}

1.3.3 离散化

例题:

无限长的数轴,数轴上每个坐标上的数都是0; n 次操作,每次操作将某一位置 x 上的数加 c;m 次询问,每个询问包含两个整数 l 和 r ,求出在区间 [l,r] 之间的所有数的和

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII; // pair 两个数据合并成一个数据(需要头文件vector)

const int N = 300010; // 开数组a,最坏的情况,下标和区间端点毫无重合,最大需要(n + 2m)的容量
int n, m;
int a[N], s[N];

vector<int> alls;
vector<PII> adds, query; // 加值和询问都是成对出现的

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 r + 1; // 加1便于求前缀和(从1开始)
}

int main()
{
    cin >> n >> m;

    for (int i = 0; i < n; i ++ )
    {
        int x, c;
        cin >> x >> c;
        adds.push_back({x, c}); // 存入加值

        alls.push_back(x); // 存入下标

    }

    for (int i = 0; i < n; i ++ )
    {
        int l, r;
        cin >> l >> r;

        query.push_back({l, r}); // 存入区间端点
        alls.push_back(l);
        alls.push_back(r);
    }

    sort(alls.begin(), alls.end()); // 按大小顺序sort

    alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去重

    for (auto item : adds)
    {
        int x = find(item.first); // 将数据存入a数组
        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), r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}

/*
// 手写unique函数
vector<int>:: iterator unique_m(vector<int> &a)
 {
    int j = 0;
    for (int i = 0; i < a.size(); i ++ )
        if (!i || a[i] != a[i - 1])
            a[j ++ ] = a[i];
    // a[0]到a[j - 1]为a中所有不重复的数;剩下的是原数组中的数
    
    return a.begin() + j; // 非重复序列最后一个元素的下一个位置
 */

1.3.4 区间合并

例题:

给定 n 个区间 [li, ri],要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII; 
vector<PII> segs;

void merge(vector<PII> &segs)
{
    vector<PII> res;
	// 排序 使区间有序
    // 使用sort,先对左端点排序,当左端点相同的时候,对右端点排序
    sort(segs.begin(), segs.end()); // 头文件algorithm

    int st = -2e9, ed = -2e9;

    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

        if (st != -2e9) res.push_back({st, ed});

        segs = res; // 将结果返回给主函数引用的segs
}

int main()
{
    int n;
    cin >> n;

    for (int i = 0; i < n; i ++ )
    {
        int l, r;
        cin >> l >> r;

        segs.push_back({l, r});
    }

    merge(segs);

    cout << segs.size() << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值