(leetcode220)存在重复元素III(滑动窗口法及其平衡二叉树改进思路、桶排序)

原题如下:
(leetcode220)存在重复元素III(滑动窗口法及其平衡二叉树改进思路、桶排序)_第1张图片

1、滑动窗口法

设置一个长度为k的滑动窗口,对窗口内元素排序,遍历找出差值最小的元素对,若小于t,则返回True,否则,若直到最后都没有返回True,则返回False。

对于t=0,做特判,因为可以根据集合的唯一性直接判断而不需要排序,尤其是针对此题的测试用例非常有效。

这份代码超过了93.8%的提交。

import copy
class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
    	#特判,排除不可能存在的几种情况
        if k<0 or t<0 or len(nums)<2:
            return False
        #对t=0特判,因为无需排序,直接利用集合唯一性可解
        elif t==0:
            l=len(nums)
            k=min(k,l)
            st=set()
            for i in range(k):
                if nums[i] in st:
                    return True
                else:
                    st.add(nums[i])
            for i in range(k,l):
                if nums[i] in st:
                    return True
                else:
                    st.remove(nums[i-k])
                    st.add(nums[i])
            return False
        #对于大多数情况,维护有序k长列表lst和无序k长集合st,
        #之所以维护st是为了让in更快,
        #因为st使用hash存储,增删改查只有O(1)的复杂度
        st=set()
        lst=[]
        for index in range(len(nums)):
            if len(st)>k:
                st.remove(lst.pop(0))
            if nums[index] in st:
                return True
            else:
                st.add(nums[index])
                lst.append(nums[index])
                tmp=copy.deepcopy(lst)
                tmp.sort()
                for index in range(len(tmp)-1):
                    if tmp[index+1]-tmp[index]<=t:
                        return True
        return False

这里必须说明的是,虽然在这个题中表现不错,但是很大程度上有测试用例的功劳。因为对长度为k的窗口排序复杂度为O(klogk),对于n的长度,整体复杂度为O(nklogk),当k接近n的时候复杂度会很大,逼近O(n*nlogn)。因此这个方法是很不稳定的,并不适合在要求严格的场合使用。

对于这种情况,为了避免排序,参考题解可以知道,我们应该在插入的时候建立平衡二叉搜索树,使得排序在建树的时候完成,删除元素在删除节点时完成。这样我们能在logk的时间复杂度里找到我们需要的元素,遗憾的是python标准库中不提供自平衡二叉搜索树,这里笔者偷个懒,如果将来有时间回来补上。

2、桶排序

参考题解链接
思路:设定至多k个桶,每个桶的装载数值范围是相邻的t+1个元素,这样只要是在一个桶里的元素相差必定<=t,因此每个桶中至多只有一个元素,超过则直接返回True。其他<=t的元素都在前一个桶或者后一个桶里面。

具体步骤:
1、对于每一个元素num,整除t+1得结果p,这样临近t+1个元素都会得到同一个p,装入键为p的桶中。
例如t+1为0时,0-2对应0,3-5对应1,6-8对应2。。。。。。。
2、如果p中没有元素,装入p桶,判断前桶和后桶中元素是否符合要求。
3、如果p原来已有元素,直接返回True,因为一个桶中的元素都符合条件。
4、如果桶的个数超过k,表示不符合相距最多为k的条件,则丢掉最早的桶,也就是k个元素之前的p桶,装入此时的新桶。
只需遍历一次元素,因此时间复杂度为O(n),空间复杂度为O(k)。

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        if k<0 or t<0 or len(nums)<2:
            return False
        dic={}
        size=t+1
        for index in range(len(nums)):
            p=nums[index]//size
            if len(dic)>k:
                dic.pop(nums[index-k-1]//size)
            #print(dic)
            if p in dic.keys():
                return True
            dic[p]=nums[index]
            if p-1 in dic and dic[p]-dic[p-1]<=t:
                return True
            if p+1 in dic and dic[p+1]-dic[p]<=t:
                return True
            #print(dic)
        return False

你可能感兴趣的:(leetcode,leetcode220,存在重复元素III,滑动窗口,平衡二叉搜索树,桶排序)