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刷题之路(五)——medium的进阶
- LeetCode刷题之路(三)——medium的进阶
- JVM菜鸟进阶高手之路八(一些细节)
- Leetcode 268. Missing Number (Medium) (cpp)
- LeetCode-48-Rotate Image(模拟)-Medium
- [Leetcode 151, Medium] Reverse Words in a String
- 【LeetCode】60.Permutation Sequence(medium)解题报告
- [Leetcode 144, Medium] Binary Tree Preorder Traversal
- Leetcode 229. Majority Element II (Medium) (cpp)
- [Leetcode 131, Medium] Palindrome Partitioning
- 【LeetCode】 Lowest Common Ancestor 最近公共祖先 - Medium
- Leetcode 150. Evaluate Reverse Polish Notation (Medium) (cpp)
- 蜗牛慢慢爬 LeetCode 2. Add Two Numbers [Difficulty: Medium]
- [Leetcode 162, Medium] Find Peak Element
- JAVA的进阶学习之路,从入门到中级
- [LeetCode] 035. Search Insert Position (Medium) (C++)
- (Leetcode)53. Maximum Subarray(medium)
- Leetcode: 48.Rotate Image(Week4, Medium)
- 【LeetCode】98. Validate Binary Search Tree(Medium)解题报告
- LeetCode刷题(C++)——Binary Tree Zigzag Level Order Traversal(Medium)