码迷,mamicode.com
首页 > 其他好文 > 详细

待字闺中之最多连续数的子集

时间:2016-04-12 22:17:37      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:

题目来源,待字闺中,原创@陈利人 ,欢迎大家继续关注微信公众账号“待字闺中”

给一个整数数组a[]。 找到当中包括最多连续数的子集,比方给:15, 7, 12, 6, 14, 13, 9, 11,则返回: 5:[11, 12, 13, 14, 15] 。

分析:最简单的方法是sort然后scan一遍,可是要 o(nlgn) , 有什么 O(n) 的方法吗?网上有人用map或set来做。但map或set的复杂度还是O(nlgn)。并查集能够做到O(n),此题用并查集比較简单。

下面參考http://www.2cto.com/kf/201308/234796.html,可是该文章给的代码有缺陷。就是仅仅能处理正数的情况

并查集是一宗简单的用途广泛的算法和数据结构。并查集是若干个不相交集合。可以实现较快的合并和推断元素所在集合的操作。应用非常多,比方:求无向图的连通分量个数,实现kruskal算法等。

并查集能够方便地进行下面三种操作:

1、Make(x):把每个元素初始化为一个集合,初始化后每个元素的父节点就是它本身。

2、Find(x):查找一个元素所在的集合,一个元素所在的集合用这个集合的祖先节点来标识。

推断两个元素是否属于同一个集合,仅仅要看他们所在集合的祖先节点是否同样就可以。

3、Union(x, y):合并x、y所在的两个集合。先利用Find()找到两个集合的祖先,若这两个祖先节点不是同一个节点,将当中一个祖先节点指向还有一个祖先节点就可以。(详细哪个祖先指向哪个祖先能够依据实际情况而定)

并查集的优化:在Find函数中,每次找祖先节点的复杂度是O(n)。当我们经过递归找祖先节点的时候。顺便把这条路径上的全部子孙节点都直接指向祖先。这样下次Find的时候复杂度就变成了O(1)。

回到题目,首先调用Make(x)将每一个元素变成一个并查集,然后一次扫描a[i],查看a[i]-1是否存在,若存在调用Union(a[i], a[i]-1)。查看a[i]+1是否存在,若存在调用Union(a[i]+1, a[i])。在合并的同一时候更新集合的大小。

接下里的问题是怎么推断a[i]-1和a[i]+1是否存在,用哈希能够解决,并且复杂度是O(1)。

该题中并查集的操作都是基于下标的。我们用p[i]表示a[i]的父节点的下标,用len[i]表示以a[i]为根的集合的大小。我们合并的时候总是将较小值集合的祖先指向较大值集合的祖先。这样就仅仅须要记录较大值集合的祖先节点相应的长度。

最后扫描数组a[]和len[]。找到最大长度maxLen相应的a[i]。最后的结果就是:a[i]-maxLen+1, a[i]-maxLen+2, ..., a[i]。

详细见代码。该代码使用map做hash表,比原文的vector优越的地方就是能够处理负数

#include <iostream>
#include <map>
#include <vector>
#include <assert.h>
using namespace std;

void Make(vector<int>& p,vector<int>& len,int x)
{
	p[x] = x;
	len[x] = 1;
}
int Find(vector<int>& p,int x)
{
	if(p[x] != x)p[x] = Find(p,p[x]);
	return p[x];
}
//保证x >= y
void Union(vector<int>& p,vector<int>& len,int x,int y)
{
	int p1 = Find(p,x);
	int p2 = Find(p,y);
	if(p1 == p2)return;
	p[p2] = p1;
	len[p1] += len[p2];
}
bool exist(map<int,int>& hash,int value,int x)
{
	map<int,int>::iterator iter = hash.begin();
	while(iter != hash.end())
	{
		if(iter->first == value && iter->second == x)return true;
		iter ++;
	}
	return false;
}
bool exist(map<int,int>& hash,int value)
{
	map<int,int>::iterator iter = hash.begin();
	while(iter != hash.end())
	{
		if(iter->first == value)return true;
		iter ++;
	}
	return false;
}
void Longest(vector<int>& ivec, int& max, int& maxLen)
{
	assert(!ivec.empty());
	int size = ivec.size();
	vector<int> p(size);//并查集的父亲下标
	vector<int> len(size);//该节点为根的并查集的大小
	map<int,int> hash;//用于推断某一值的相邻元素是否存在
	int i;
	for(i=0;i<size;i++)Make(p,len,i);
	for(i=0;i<size;i++) hash[ivec[i]] = i;//假设有反复数据,后面的数据会把前面的数据覆盖。这样能够防止反复计算
	for(i=0;i<size;i++)
	{
		if(exist(hash,ivec[i],i))//加上i用于处理反复数字
		{
			if(exist(hash,ivec[i]-1))Union(p,len,i,hash[ivec[i]-1]);//此处不须要处理反复数字是由于hash[ivec[i]-1]选中的肯定是最后一个
			if(exist(hash,ivec[i]+1))Union(p,len,hash[ivec[i]+1],i);//交换位置保证Union的x > y
		}
	}
	max = ivec[0];
	maxLen = len[0];
	for(i=1;i<size;i++)
	{
		if(len[i] >maxLen)</span>
		{
			max = ivec[i];
			maxLen = len[i];
		}
	}
}
int main()
{
	int n;
	while(cin >> n)
	{
		vector<int> data(n);
		int i;
		for(i=0;i<n;i++)cin >> data[i];
		int max,maxLen;
		Longest(data,max,maxLen);
		for(i=1;i<=maxLen;i++)cout << max - maxLen + i << " ";
		cout << endl;
	}
	return 0;
}
如有错误,请指正,谢谢


待字闺中之最多连续数的子集

标签:

原文地址:http://www.cnblogs.com/lcchuguo/p/5384578.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!