LeetCode刷题之路(四)——medium的进阶

Problem 29:Divide Two Integers

  不使用除法、乘法和求余运算,完成两个数的除法,当数值溢出时,返回MAX_INT

  解题思路:第一想法,直接用被除数循环减去除数,每次减得的结果大于0,则结果加1,小于0时循环结束。解法没问题,但是复杂度太高,当用一个很大的数去除一个很小的数时,会超时。
  于是我们想到,我每次不是减去除数,而是依次减去除数的2的指数次幂倍,一直看能减到多少结果小于0,然后我们记录这个是2的多少倍,并对剩余值,继续重复这个操作:
  譬如13除以4,13>4,4<<1为8;13>8,8<<1为16;13<16,记录res=2=4,剩余13-8=7,然后将7看做13,重复操作。
注意越界操作的处理。

  def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        sign = (dividend > 0) == (divisor > 0)
        dividend = abs(dividend)
        divisor = abs(divisor)
        res = 0
        while dividend >= divisor:
            t, c = divisor, 1
            while dividend >= t:
                t <<= 1
                c <<= 1
            t >>= 1
            c >>= 1
            dividend -= t
            res += c
        if not sign:
            res = - res
        return min(max(res, -2147483648),2147483647)

Problem 33:Search in Rotated Sorted Array

  在一个rotated过的排序数组中,找到target值的下标,如果不存在。返回-1,譬如一个排血数组[1,2,3,4,5,6,7],rotated过后可能变成[5,6,7,1,2,3,4],数组中没有重复元素

  解题思路:很明显,这题是要用类似二分查找的方法,得到一个O(logN)的解法,不同于在排序数组中找,我们的判断条件要稍微复杂一点,因为他们可能不在一个单调区间。
  考虑数组[4,6,7,0,1,2,3],l=0,r=6,target为6,此时nums[mid]=0<6,按照为旋转之前的话,l应该更新为3,但是在这显然不成立。我们分情况讨论:

  • 当num[0]>target,此时,target只可能出现在旋转后的部分,即上述例子的…0,1,2,3部分,如果此时num[0] num[mid],代表mid之前已经旋转,如果target>num[mid],l要更新为mid+1,否则,r=mid
  • 当num[0]

    综上,我们发现,l需要被更新为mid+1的时候,共有如下情况:

  • num[0]>target and num[0]

  • num[0]>target and num[0]>num[mid] and target>num[mid]
  • num[0]num[mid]

即,上述三个条件的异或为True的时候,更新l=mid+1,否则更新r=mid
其实,三个条件的排列组合共有8种,其中三种是更新l=mid+1的,三种更新r=mid的,还有两种是不存在的情况。

  def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        l, r = 0, len(nums) - 1
        while l < r:
            mid = (l + r) / 2
            if (nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]):
                l = mid + 1
            else:
                r = mid
        return l if target in nums[l:l+1] else -1

Problem 34:Search for a Range

  给定一个升序排列的数字,找到target出现的区间,如果没有,返回[-1,-1],譬如[1,2,3,4,6,6,8],target=6,则返回[4,5],要求时间复杂度为O(logn)

  解题思路:(心累,之前写了没保存了,这里就大概说一下吧)时间复杂度要求为二分查找,依次找到左右边界,按照下述二分查找的策略,最终得到的结果一定是左边界(如果target存在的话),查找右边界的方式同理,只需将下述策略的所有l和r互换之后看即可:即l=mid+1,替换为r=mid-1;r=mid替换为l=mid
查找左边界

    l,r = 0, len(nums)-1
    while l < r:
        mid = (l + r) //2
        if target > nums[mid]:
            l = mid + 1           
        else:
            r = mid

  l和r即是左边界
查找右边界

    l,r = l, len(nums)-1
        while l < r:
            mid = (l + r + 1)//2
            if target < nums[mid]:
                r = mid - 1
            else:
                l = mid

  此处的l和r即是左边界,需要注意的是求mid的加1操作,因为整除会向下取整,因此查找左边的最后一次循环,l是不动的,由r向l靠拢,求解右边界的时候正好相反,我们需要r不动,l向r靠拢,因此需要加1。


Problem 36:Valid Sudoku

  给定一个初始化的数独棋盘,判断是否有效,即每个数字在每一行、每一列和每一个3*3的单元内是否只出现了一次,没有被填充的单元值为’.’

  解题思路:(心累,之前写了没保存了,这里就大概说一下吧)开始理解错,其实只用遍历棋盘,记录每个数字出现的位置(‘.’的不用管),遍历结束后判断是否满足要求即可,python一行搞定

    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        pos = sum(([(d, r), (c, d), (d, r/3, c/3)] 
                    for r, row in enumerate(board) 
                    for c, d in enumerate(row) if d != '.'), [])
        return len(pos) == len(set(pos))

  注意,前两个二元组不能写成(d,r),(d,c)不然无法标识行还是列


你可能感兴趣的:(leetcode)