LeetCode-128.最长连续序列 哈希表法

这里是题目描述:LeetCOde-最长连续序列

如果题干中不对时间复杂度做 O(n) 的限制的话,这道题没有什么难度,我们可以对数组排序后使用双指针(或者基于贪心算法)来解本题,时间复杂度为O(nlogn)。但是加上时间复杂度不超过 O(n) 这一限制后,我们不能再采用先排序再贪心的方法,因为大多数排序算法的时间复杂度都为O(nlogn),计数排序理论上的时间复杂度为O(n),但是这有限制条件——被排序的数组必须足够“紧凑”,如果被排序数组过于“稀疏”时间复杂度便不再是O(n)

我们借助哈希表来实现 O(n) 时间复杂度下对本题的解。将输入数组nums的包含的所有数字存入哈希表中,这有两个目的:一是去除nums中的重复数字,二是可以在 O(1) 时间开销下确定某个数字是否存在于数组nums中。基于哈希表的初步方法:将nums中的数字存入哈希表中;然后遍历数组nums(也可以使用foreach遍历哈希表的键),然后确定以当前遍历到的数字x为开头的连续序列长度,从x+1开始判断x+1是否存在于哈希表中,接下来判断x+2x+3… 直到x+y不存在于哈希表中,则此连续序列最大的数字是x+y-1,则该子序列长度是y,将所有的这些子序列中最大的长度作为最终结果返回。

但这种方法的时间复杂度仍然为O(n2),原因在于:我们确定了以x为开头,以x+y-1为结尾的长度为y的连续序列x~x+y-1后,但仍然会遍历到数字x+m (0,以它开头的连续序列为x+m~x+y-1,实际上包含在x~x+y-1中,且肯定不是最长连续序列,实际上我们对一个连续序列的子序列做了无用而重复的搜索。为了避免这种重复,需要保证对每一段连续序列只做一次搜索,我们采用下面两种方法来实现

基于哈希表的方法1

对于遍历到的一个数字x,我们首先判断nums中是否存在它的前驱数字x-1,如果存在,证明x不是一个连续序列的第一个数字,跳过x;如果不存在x-1,则x是一个连续序列的第一个数字,不跳过,开始搜索x+1x+2…是否存在来确定子序列长度。这样保证了每个连续子序列只被搜索一遍,时间复杂度为O(n)

基于哈希表方法1:

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length<=1)
        {
            return nums.length;
        }
        //存储nums中去重后的元素
        HashSet<Integer> hashSet=new HashSet<>();
        for(int i=0;i<nums.length;i++)
        {
            hashSet.add(nums[i]);
        }
        //开始寻找最长连续序列的长度
        int maxLen=0;
        for(int e:hashSet)
        {
            if(!hashSet.contains(e-1)) //当前的数字不存在前驱,是一个连续序列的首个数字
            {
                int len=0;
                for(int i=e;;i++)
                {
                    if(hashSet.contains(i))
                    {
                        len++;
                    }
                    else
                    {
                        break;
                    }
                }
                maxLen=Math.max(maxLen,len);
            }
        }
        return maxLen;
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

基于哈希表的方法2

记录哈希表中的数字是否已经被遍历过,如果数字x被遍历过,表示它所在的连续序列已近被搜索过不需要再搜索一遍,直接跳过x;如果x没有被遍历过,则开始搜索x所在的连续序列的长度,和上面的只向后搜索不同,这里需要对x本身,和它左右两边的数字都进行搜索并记录它们已经被遍历过,确定x所在连续序列长度

基于哈希表方法2:

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length<=1)
        {
            return nums.length;
        }
        //存储nums中去重后的元素,并记录它们是否已经被寻找过
        HashMap<Integer,Boolean> hashMap=new HashMap<>();
        for(int i=0;i<nums.length;i++)
        {
            hashMap.put(nums[i],false);
        }
        //开始寻找最长最长连续序列的长度
        int maxLen=0;
        for(int k:hashMap.keySet())
        {
            if(!hashMap.get(k)) //还没有被寻找过,表示k所在的连续序列还没有被找过
            {
                int len=0;
                for(int i=k-1;;i--) //寻找连续序列中位于k前面的数字
                {
                    if(hashMap.containsKey(i))
                    {
                        hashMap.put(i,true);
                        len++;
                    }
                    else
                    {
                        break;
                    }
                }
                for(int i=k;;i++) //寻找连续序列中位于k后面的数字
                {
                    if(hashMap.containsKey(i))
                    {
                        hashMap.put(i,true);
                        len++;
                    }
                    else
                    {
                        break;
                    }
                }
                maxLen=Math.max(maxLen,len);
            }
        }
        return maxLen;
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

你可能感兴趣的:(算法,数据结构,java,leetcode,哈希)