給你一個未排序的數組,要在O(n)的時間複雜度內求出最長的由數組中元素組成的連續序列的長度。
最直觀的就是排序之後掃一遍,但這種方式時間複雜度為O(nlogn),明顯不符合要求。
假設我們碰到一個數i,要求包含這個數的連續序列的長度
我們會去向左找i-1、i-2、i-3…是否在數組中,再向右找i+1、i+2、i+3…是否在序列中,直到找到第一個不在數組中的為止,然後即可知道i所在序列的長度。
因此我們可以利用hash表,先將數組中所有數存到hash表中,然後對數組中的數掃一遍,每個數依次為中心向左向右檢查是否在hash表中,直到掃到第一個不在hash表中的為止。
當然,如果要開始掃的時候,這個數之前已經檢查過了,那就無需再掃了,避免對一個連續序列重複掃描。
這裡hash表用unordered_map實現,時間複雜度為O(n)+O(n*hash表掃描效率),這裡hash表掃描效率可以近似看成O(1)。
算法的關鍵點在於能否保證所有連續序列都正確掃描一遍。
因為每個數都會在一個連續序列中,因此每個連續序列是可以保證都檢查一遍的,向左向右一個一個檢查也不會將長度算錯,因此上述算法是正確的。
下面舉一個簡單例子走一遍算法幫助理解:[100,4,200,1,3,2]。
初始化:ans=0,將100、4、200、1、3、2扔到unordered_map中,是否掃描的狀態記為false;
掃描第一個數100:,還未掃描,向左:99不在unordered_map中,向右:101不在unordered_map中,ans=max(ans,101-99-1)=1;
掃描第二個數4,還未掃描,向左:3、2、1在unordered_map中0不在unordered_map中,向右:5不在unordered_map中,ans=max(ans,5-0-1)=4;
掃描第三個數200,已經掃描,跳過;
掃描第四個數1,已經掃描,跳過;
掃描第五個數3,已經掃描,跳過;
掃描第六個數2,已經掃描,跳過;
最終結果為ans=4。
int longestConsecutive(vector& nums)
{
int ans=0;
int len=nums.size();
unordered_map<int,bool> used;
for(int i=0;i<len;i++)
{
used[nums[i]]=false;
}
for(int i=0;i<len;i++)
{
if(used[nums[i]])continue;
used[nums[i]]=true;
int left=nums[i]-1,right=nums[i]+1;
while(used.find(left)!=used.end())
{
used[left]=true;
left--;
}
while(used.find(right)!=used.end())
{
used[right]=true;
right++;
}
ans=max(ans,right-left-1);
}
return ans;
}
作者:東大ACM退役隊伍
編輯:劉凱旋
推薦閱讀:
leetcode刷題指南之WordLadderII
leetcode刷題指南之Triangle
leetcode刷題指南之Best Time to Buy and Sell Stock