算法(4)——区间和,区间合并,单链表

本文介绍了如何在数值范围大且元素较少的情况下对整数进行离散化,通过二分法处理重复元素,并以AcWing题库中的区间和问题和单链表操作为例,展示了相关算法的实现与应用。
摘要由CSDN通过智能技术生成

区间和(整数的离散化):

使用条件:

1.数值范围过大:0~1e9(称呼为数_1)

​ 2.数的个数较少

则需将数_1(n个)映射到从0~n-1的自然数上(将数_1存入数组)

如:a[1,10,200,3000,500000]共5个数,将其映射到从0开始的5个数,该过程被称为离散化

       ​ b 0,1,,,2,,,,,,,,3,,,,,,,,,,4

问题:

  1. a[]中可能有重复元素 => 去重(a有序)

  2. 如何算出x离散化后的值 => 二分

去重:

vector<int> alls;
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());//去重

解释:

unique(a.begin(),a.end()) 会将a数组中的重复元素移到a数组的最后面,并返回非多余元素的最后一个坐标  

erase(a.begin(),a.end()) 会将a数组从begin,到end的元素删除 => 删除多余元素

练习及代码:

802. 区间和 - AcWing题库

代码:

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

using namespace std;

typedef pair<int, int> PII;

const int N = 300010;

int m, n;
int a[N], s[N];//a数组在数轴x的对应数组下标上储存c值;s数组记录a数组的各个前缀和

vector<int> alls;//alls储存有数的坐标x;储存需要询问的位点l,r;共n+2m个坐标点位=>300010
vector<PII> add, query;//add记录第x位数加c;query储存询问的左右区间;

int find(int x)//找到坐标点位x(数轴)在点位储存集alls中的第几个(数组坐标);数轴坐标通过find()函数得到它在alls(a/s)数组的下标(-1)
{
	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;
		add.push_back({ x,c });

		alls.push_back(x);
	}

	for (int i = 0; i < m; 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());
	alls.erase(unique(alls.begin(), alls.end()), alls.end());
	//处理插入
	for (auto item : add)//auto i = l;用于自动识别类型;for (auto item : add):依次取add中的元素(复制),赋值给item
	{
		int x = find(item.first);//item.first取add中的坐标x;find(x)
		a[x] += item.second;//在a中对应的数组下标上储存c的值
	}
	//预处理前缀和
	for (int i = 1; i <= alls.size(); i++)
	{
		s[i] = s[i - 1] + a[i];//将各个下标的前缀和记录在s数组中
	}
	//处理询问
	for (auto item : query)//依次取出需要询问的左右边界
	{
		int l = find(item.first), r = find(item.second);
		cout << s[r] - s[l - 1] << endl;
	}
	return 0;

}

区间合并:

问题描述:803. 区间合并 - AcWing题库

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

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

输出合并完成后的区间个数。

例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。

方法:

  1. 所有区间按区间的左端点排序

  2. 扫描所有区间,并且将肯有交集的区间进行合并

代码:

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

using namespace std;

typedef pair<int, int> PII;

int n;
vector<PII> q;


void merge(vector<PII>& q)
{
	vector<PII> res;
	sort(q.begin(), q.end());

	int st = -2e9, ed = -2e9;
	for (auto x : q)
	{
		if (ed < x.first)//不能合并,需更新时进入
		{
			if (st != -2e9) res.push_back({ st,ed });//有区间时将区间存入res中,无区间时直接更新
			st = x.first, ed = x.second;//更新st和ed
		}
		else ed = max(ed, x.second);//可以合并时,更新ed
	}
	if (st != -2e9) res.push_back({ st,ed });//将最后一个区间存入res中
	q = res;
}

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int l, r;
		cin >> l >> r;
		q.push_back({ l,r });
	}
	merge(q);
	
	cout << q.size() << endl;
	return 0;
}

单链表:

概念:

如上图:单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。

通常是用结构体加指针的形式来使用单链表,但开辟一个新空间耗时太长,于是使用数组来模拟单链表的用法:

如上图,定义两个数组  e[N]  en[N],e[N]存数,en[[N]存地址(坐标)

练习:

826. 单链表 - AcWing题库

代码:

#include<iostream>

using namespace std;

const int N = 100010;

//head表示头结点的下标,e存数,ne存下标
//e[i]表示i结点的值
//ne[i]表示结点i的next指针是多少
//idx储存当前用到了那个地址(下标);当前可以用的最新的下标
int head, e[N], ne[N],idx;

//初始化
void init()
{
	head = -1;
	idx = 0;
}

//在头结点插入x
void add_to_head(int x)
{
	e[idx] = x;
	ne[idx] = head;
	head = idx;
	idx++;
}

//将x插到下标是k的位置的后面(第几个输入的数与idx的下标-1对应)
void add(int k, int x)
{
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}
//将下标是k的后面的点删掉
void remove(int k)
{
	ne[k] = ne[ne[k]];
}

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

	init();//初始化

	while (m--)
	{
		int k, x;
		char op;//option

		cin >> op;
		if (op == 'H')
		{
			cin >> x;
			add_to_head(x);
		}
		else if (op == 'D')
		{
			cin >> k;
			if (k == 0) head = ne[head];//删除头结点
			else remove(k - 1);//第k个数,存时从下标为0开始存,因此减1
		}
		else if (op == 'I')
		{
			cin >> k >> x;
			add(k - 1, x);
		}
	}
	for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';//注意如何遍历链表
	cout << endl;

	return 0;
}

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值