您的位置:首页 > 其它

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

2018-03-20 17:19 393 查看

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;如果此时num[0]> num[mid],代表mid之前已经旋转,如果target>num[mid],l要更新为mid+1,否则,r=mid

当num[0]<target时同理

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

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

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

num[0]<target and num[0]<num[mid] and target>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