程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 最多連續數的子集

最多連續數的子集

編輯:C++入門知識

給一個整數數組a[], 找到其中包含最多連續數的子集,比如給:15, 7, 12, 6, 14, 13, 9, 11,則返回: 5:[11, 12, 13, 14, 15] 。

最簡單的方法是sort然後scan一遍,但是要 o(nlgn) , 有什麼 O(n) 的方法嗎?

思路:

網上有人用map來做,個人覺得用map的復雜度還是O(nlgn)。並查集可以做到O(n),但網上一直沒有看到完整的代碼,所以自己寫了一個。

先簡單介紹並查集的內容,算法導論和網上都可以找到相應的資料。

並查集是一宗簡單的用途廣泛的算法和數據結構。並查集是若干個不相交集合,能夠實現較快的合並和判斷元素所在集合的操作。應用很多,比如:求無向圖的連通分量個數,實現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]。

 

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

void Make(vector<int>& p, vector<int>& len, int x)
{
	p[x] = x;		//x是下標
	len[x] = 1;		//一個節點的集合長度為1
}

int Find(vector<int>& p, int x)
{
	if (x != p[x])
		p[x] = Find(p, p[x]);	//路徑壓縮,將該路徑上所有子孫節點,即集合中所有子孫節點的父節點都為根節點
	return p[x];
}

void Union(vector<int>& p, vector<int>& len, int x, int y)
{//傳參的時候,要將較大值的節點傳給x,較小值的節點傳給y
	int px = Find(p, x);
	int py = Find(p, y);
	if (px == py)
		return;
	p[py] = px;			//將py指向px
	len[px] += len[py];	//px為新的祖先,px的值要大於py的值,所以只更新px的長度即可
}

void Longest(vector<int>& ivec, int& max, int& maxLen)
{
	assert(!ivec.empty());
	int size = ivec.size();
	vector<int> p(size);
	vector<int> len(size);
	for (int i = 0; i < size; ++i)
		Make(p, len, i);
	int MAX = ivec[0];
	for (int i = 1; i < size; ++i)
	{
		if (ivec[i] > MAX)
			MAX = ivec[i];
	}
	vector<int> hash((MAX+2), -1);	//用於查找a[i]-1和a[i]+1是否存在
	for (int i = 0; i < size; ++i)
		hash[ivec[i]] = i;

	for (int i = 0; i < size; ++i)
	{
		int num = ivec[i];
		if (hash[num] == i)	//這個判斷條件用於處理重復數字,若hash[num]!=i,說明在i之後還有num重復出現,只處理最後一個即可
		{
			if (hash[num-1] != -1)
				Union(p, len, i, hash[num-1]);
			if (hash[num+1] != -1)
				Union(p, len, hash[num+1], i);
		}
	}
	max = ivec[0];
	maxLen = len[0];
	for (int i = 1; i < size; ++i)
	{
		if (len[i] > maxLen)
		{
			maxLen = len[i];
			max = ivec[i];
		}
	}
}

int main()
{
	int a[] = {15, 7, 12, 6, 14, 13, 9, 11};
	vector<int> ivec(a, a + 8);
	int max, maxLen;
	Longest(ivec, max, maxLen);
	for (int i = max-maxLen+1; i <= max; ++i)
		cout << i << ' ';
	cout << endl;
	return 0;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved