zoukankan      html  css  js  c++  java
  • 待字闺中之最多连续数的子集

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

    给一个整数数组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;
    }
    如有错误,请指正,谢谢


  • 相关阅读:
    break return continue
    爬虫---请求
    pycharm加开头注释
    爬虫---入门
    pip
    XML基础
    英语
    布局
    adobe
    StackOverflow
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5384578.html
Copyright © 2011-2022 走看看