Leetcode题medium11/15/16/18/31/33/34/39/40,Python多种解法(三)

前文

  继上一篇:Leetcode题121/122/167/169/189/217/219/268/283,Python多种解法(二),这次开始分享数组medium的题,说实在的,到了中等题,很难自己过了,只有少部分和easy一样难度的题才能过,打击还是有的,不过还是继续刷吧,等二刷三刷的时候,相信自己的代码能力和算法能力应该会有提高的!那开始上题了!

11. Container With Most Water

class Solution1:
    """
    从首尾开始计算初步的最大值,然后从首尾短的那边开始往中间推移,不断地计算最大,直到中间汇合则break出来,有点类似于贪心算法,即一直取当前最大的面积.
    Runtime: 60 ms, faster than 79.17% of Python3 online submissions for Container With Most Water.
    """
    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        start,end,now_max = 0,len(height)-1,0
        while start < end:
            now_max = max(now_max, (end-start)*min(height[start], height[end]))
            if height[end] < height[start]:
                end -= 1
            else:
                start += 1
        return now_max



class Solution2:
    """
    讨论区的解法,时间复杂度要优于上方,即从0开始,分为i\j从两端开始往里收缩,原理和上方差不多,但速度就是要快一些.
    Runtime: 48 ms, faster than 100.00% of Python3 online submissions for Container With Most Water.
    """
    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        area, i, j = 0, 0, len(height) - 1
        while i < j:
            if height[i] <= height[j]:
                less = height[i]
                i += 1
            else:
                less = height[j]
                j -= 1
            new = (j - i + 1) * less
            if new > area:
                area = new
        return area
15. 3Sum
class Solution:
    """
    相当于利用快排的原理,先排序然后从头选定一个数,接着从剩下的数里依次从首尾选择两个数,如果满足条件和为0,则缩小范围继续;没满足也缩小范围继续。
    Runtime: 700 ms, faster than 93.20% of Python3 online submissions for 3Sum.
    Memory Usage: 15.9 MB, less than 100.00% of Python3 online submissions for 3Sum.
    """
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []
        nums.sort()
        for i in range(len(nums)):
            if i>0 and nums[i] == nums[i-1]:
                continue
            target = -nums[i]
            l, r = i+1, len(nums)-1
            while l < r:
                ans = nums[l] + nums[r]
                if ans == target:
                    res.append([nums[i], nums[l], nums[r]])
                    while l<r and nums[l] == nums[l+1]:
                        l = l + 1
                    l += 1
                    while l<r and nums[r] == nums[r-1]:
                        r -= 1
                    r -= 1
                elif ans > target:
                    r -= 1
                else:
                    l += 1
        return res
16. 3Sum Closest

class Solution(object):
    """
    和15题的思路差不多,利用先求得三者的和,然后进行与target最小值的判断,保持当前最小值sum1,然后逐步推进数组,直到l>=r,所以是个O(n^2)
    Runtime: 72 ms, faster than 75.37% of Python online submissions for 3Sum Closest.
    Memory Usage: 11 MB, less than 100.00% of Python online submissions for 3Sum Closest.
    """
    def threeSumClosest(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        nums.sort()
        n = len(nums)
        sum1 = nums[0] + nums[1] + nums[2]
        for i in range(n):
            l,r = i+1, n-1
            while l < r:
                res = nums[i] + nums[l] + nums[r]
                if abs(sum1-target) > abs(res-target):
                    sum1 = res
                if res < target:
                    while l < r and nums[l]==nums[l-1]:
                        l += 1
                    l += 1
                else:
                    if res == target:
                        return res
                    while l < r and nums[r] == nums[r-1]:
                        r -= 1
                    r -= 1
        return sum1
18. 4Sum
import collections
import itertools


class Solution(object):
    """
    直接沿用3Sum的解法,多添加一层罢了,所以效率也是低的不行,不过这种方法算是最容易想出的。
    Runtime: 808 ms, faster than 22.63% of Python online submissions for 4Sum.
    Memory Usage: 10.7 MB, less than 100.00% of Python online submissions for 4Sum.
    """
    def fourSum(self, nums, target):
        nums.sort()
        ans = []
        for a in range(len(nums)-3):
            if a > 0 and nums[a] == nums[a-1]:
                continue
            for b in range(a+1,len(nums)-2):
                if b > a+1 and nums[b] == nums[b-1]:
                    continue
                c, d = b+1, len(nums)-1
                while c < d:
                    tot = nums[a]+nums[b]+nums[c]+nums[d]
                    if tot == target:
                        ans.append([nums[a],nums[b],nums[c],nums[d]])
                    if tot <= target:
                        c += 1
                        while nums[c] == nums[c-1] and c < d:
                            c += 1
                    if tot >= target:
                        d -= 1
                        while nums[d] == nums[d+1] and c < d:
                            d -= 1
        return ans


class Solution2(object):
    """
    这里采用python的高级解法,这些用法看的我眼花缭乱,感觉自己学了一个假python;
    通过defaultdict建立字典,可以在key不存在时不报错,而是默认value为list即空列表;
    用combinations找出所有的index、value能构成的二元元祖;
    接着遍历two_sum的key,也就是nums任意两个数的和,因为value是set,所以接下来都是围绕set的操作;
    set.isdisjoint是求交集,即两个set没有相同的部分返回真,所以完美规避了两个pair相同;
    pair1|pair2则是求并集,即两个set的所有元素,;
    这里的sorted也是必备的,需要把不同的索引但是重复的数字给删除了;
    关于del two_sum[t],也可以不删除,因为去掉这句代码照常运行,不过速度会慢点,这点还不清楚原因。
    Runtime: 168 ms, faster than 70.47% of Python online submissions for 4Sum.
    Memory Usage: 23.5 MB, less than 100.00% of Python online submissions for 4Sum.
    """
    def fourSum(self, nums, target):
        two_sum = collections.defaultdict(list)
        res = set()
        for (n1, i1), (n2, i2) in itertools.combinations(enumerate(nums), 2):
            two_sum[i1+i2].append({n1, n2})
        for t in list(two_sum.keys()):
            if not two_sum[target-t]:
                continue
            for pair1 in two_sum[t]:
                for pair2 in two_sum[target-t]:
                    if pair1.isdisjoint(pair2):
                        res.add(tuple(sorted(nums[i] for i in pair1 | pair2)))
            del two_sum[t]
        return [list(r) for r in res]
31. Next Permutation
class Solution(object):
    """
    这道题光是题目就难倒了我,简单查了下,原来是找列表的所有排列情况,并且第一行默认是asc排序,最后一行是desc,依次排列出所有情况,
    然后给出任一行,推测出它的下一行,比如[1,2,3]就有以下情况:
    1 2 3
    1 3 2
    2 1 3
    2 3 1
    3 1 2
    3 2 1
    我个人是想了非常多的if..else..,结果提交时还是各种各样的错,最后看了网上答案,原来就是将数学规律写成代码;
    数学规律:6 5 4 8 7 3 1
    以上面为例,从后往前看,依次递增,直到4的时候结束,那么此时找出从4之后的所有数里比4大的最小数,交换他们,然后升序排序索引2(也就是原来4)
    仔细看代码,就是完完整整依据上面的逻辑,不过为了方便,是先将4之后的排序,再交换位置,道理是一样的。
    至于如何推导的,我是推导了下,不过比较麻烦,就不多说了,大家可以借助我函数里的三种情况进行推导,就是从后往前,判断每种情况下的变化
    Runtime: 28 ms, faster than 100.00% of Python online submissions for Next Permutation.
    Memory Usage: 10.8 MB, less than 100.00% of Python online submissions for Next Permutation.    
    """
    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        6 5 4 1 3 7 8 
        6 5 4 1 7 8 3
        6 5 4 8 7 3 1
        """
        n = len(nums)
        i = n - 1
        while i > 0 and nums[i] <= nums[i - 1]:
            i -= 1
        self.reverse(nums, i, n - 1)
        if i > 0:
            for j in range(i, n):
                if nums[j] > nums[i-1]:
                    self.swap(nums, i-1, j)
                    break
        
    def reverse(self, nums, i, j):
        """
        contains i and j.
        """
        for k in range(i, (i + j) / 2 + 1):
            self.swap(nums, k, i + j - k)

        
    def swap(self, nums, i, j):
        """
        contains i and j.
        """
        nums[i], nums[j] = nums[j], nums[i]
33. Search in Rotated Sorted Array

class Solution(object):
    """
    不知道有没有和我一样的,想用最原始的方法来做,即先找出被颠倒的那个数,然后再用二分法分左右两次来求解
    虽然超时了,不过这个方法真是第一时间在脑海里浮现,由此看来,还得多练
    Runtime:time limit exceed
    """
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        start = 0 
        end = len(nums)
        while start < end:
            mid = (start+end)/2
            if nums[mid] < nums[mid-1]:
                res = mid
                break
            elif nums[mid] > nums[start]:
                end = mid
            elif nums[mid] < nums[start]:
                start = mid
        if target > nums[0]:
            start = 0
            end = res
            while start < end:
                mid = (start+end)/2
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    start = mid
                else:
                    end = mid
            return -1
        else:
            start = res
            end = len(nums)
            while start < end:
                mid = (start+end)/2
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    start = mid
                else:
                    end = mid
            return -1


class Solution2(object):
    """
    讨论区的解法有两种,一种是利用二进制位运算求解,这次暂不研究,这边选取二分法,也是最容易浮现的解法
    在判断target>nums[0]时,一开始我的想法是只要判断target>nums[mid]就行了,结果出错,因为有两种情况,
    举例来说,target为4,一种是:4,5,0,1,2,此时因为中位数也就是0小于nums[0],所以应该是end = mid-1
            另一种target为7,排列是:4,5,6,7,1,2,此时中位数也就是6大于nums[0],所以应该是start=mid+1
    Runtime: 20 ms, faster than 100.00% of Python online submissions for Search in Rotated Sorted Array.
    Memory Usage: 10.8 MB, less than 93.14% of Python online submissions for Search in Rotated Sorted Array.
    """
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        start = 0 
        end = len(nums)-1
        while start <= end:
            mid = (start+end)/2
            if target == nums[mid]:
                return mid
            if target >= nums[0]:
                if target > nums[mid] and nums[mid] >= nums[0]:
                    start = mid + 1
                else:
                    end = mid - 1
            else:
                if target > nums[mid] or nums[mid] >= nums[0]:
                    start = mid + 1
                else:
                    end = mid - 1
        return -1
34. Find First and Last Position of Element in Sorted Array
class MySolution(object):
    """
    正常思路,利用二分法来完成,缺点是在找到中间数后要一次从左、从右开始遍历,这也造成了时间复杂度不是很理想
    Runtime: 24 ms, faster than 82.77% of Python online submissions for Find First and Last Position of Element in Sorted Array.
    Memory Usage: 11.4 MB, less than 60.93% of Python online submissions for Find First and Last Position of Element in Sorted Array.
    """
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        if nums == []:
            return [-1, -1]
        left_pos = 0
        right_pos = len(nums)
        
        while left_pos < right_pos:
            middle_pos = (right_pos - left_pos) // 2 + left_pos
            middle_val = nums[middle_pos]
            
            if middle_val == target:
                left = middle_pos
                while left >= 0 and nums[left] == target:
                    left -= 1
                right = middle_pos
                while right < len(nums) and nums[right] == target:
                    right += 1
                return [left + 1, right - 1]
            elif middle_val < target:
                left_pos = middle_pos + 1
            else:  # middle_val > target:
                right_pos = middle_pos
        return [-1, -1]


class Solution(object):
    """
    利用python语法的index来完成,通过两次index来定位目标位置即可,找不到报错就拦截
    Runtime: 20 ms, faster than 100.00% of Python online submissions for Find First and Last Position of Element in Sorted Array.
    Memory Usage: 11.9 MB, less than 5.30% of Python online submissions for Find First and Last Position of Element in Sorted Array.
    """
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        try:
            return [nums.index(target),len(nums)-list(reversed(nums)).index(target)-1]
        except:
            return [-1,-1]
39. Combination Sum

class Solution(object):
    """
    这道题利用了递归的思想,在函数里再次调用函数来判断target-candidates[i]的数是否在集合里,从而可以知道重复数的排列集合
    利用到递归的都相对比较难以理解,下列解法用debug或者在纸上跑一遍,会发现程序走的和预想的有出入,比如把下述代码的print注释去掉,看下结果是否如你所料?
    Runtime: 204 ms, faster than 11.86% of Python online submissions for Combination Sum.
    Memory Usage: 10.8 MB, less than 33.60% of Python online submissions for Combination Sum.
    """
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # global final_result
        final_result = []
        candidates.sort()

        def findCom(target, temp_list):
            if target in candidates:
                a = temp_list + [target]
                if sorted(a) not in final_result:
                    final_result.append(a)
            for i in [n for n in candidates if n < target]:
                # print (i, target,temp_list)
                a = temp_list + [i]
                findCom(target - i, a)

        t = []
        findCom(target, t)
        return final_result


class Solution1(object):
    """
    讨论区流行解法,用到的也是递归,而方法的效率就比上一个高多了,从命名来看是DFS,即Depth-First-Search,深度优先算法
    本质我觉得和上一个解法差不多,而且可理解性还没有上个解法好,也是从头开始遍历,然后如果target == 0,也就是找到组合,就往
    res里增加组合,没找到,就往path里增加数,直到target<0,则此次递归结束,开始下一个数的递归
    Runtime: 92 ms, faster than 47.88% of Python online submissions for Combination Sum.
    Memory Usage: 10.6 MB, less than 94.71% of Python online submissions for Combination Sum.
    """
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        res = []
        candidates.sort()
        self.dfs(candidates, target, 0, [], res)
        return res

    def dfs(self, candidates, target, index, path, res):
        if target < 0:
            return  
        if target == 0:
            res.append(path)
            return 
        for i in range(index, len(candidates)):
            self.dfs(candidates, target-candidates[i], i, path+[candidates[i]], res)


class Solution2(object):
    """
    回溯解法,非递归
    Runtime: 152 ms, faster than 17.24% of Python online submissions for Combination Sum.
    Memory Usage: 10.9 MB, less than 29.63% of Python online submissions for Combination Sum.
    """
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        candidates, res, stack, lenth=sorted(set(candidates)), [], [(0, [], target)], len(candidates)
        while stack:
            i, temp, tar=stack.pop()
            while i<lenth and tar>0:
                if candidates[i]==tar:res+=temp+[candidates[i]],
                stack+=(i, temp+[candidates[i]], tar-candidates[i]),
                i+=1

        return res
40. Combination Sum II

class Solution(object):
    """
    直接修改39的代码,在for下面增加一个判断,比如示例:[10,1,2,7,6,1,5],排完序后为[1,1,2,5,6,7,10]
    通过递归直接开始找[1,1]后面的:[1,1,2,5],[1,1,2,6],[1,1,2,7],[1,1,2,10];实际可以debug跑一遍或者纸上
    运行一遍,不过递归的确浪费了不少步骤,但这种题递归是最容易想到的解法
    Runtime: 84 ms, faster than 43.76% of Python online submissions for Combination Sum II.
    Memory Usage: 10.8 MB, less than 35.21% of Python online submissions for Combination Sum II.
    """
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        res = []
        candidates.sort()
        self.dfs(candidates, target, 0, [], res)
        return res
        
    def dfs(self, candidates, target, index, path, res):
        if target < 0:
            return  # backtracking
        if target == 0:
            res.append(path)
            return  # backtracking 
        for i in range(index, len(candidates)):
            if i > index and candidates[i] == candidates[i-1]:
                continue
            self.dfs(candidates, target-candidates[i], i+1, path+[candidates[i]], res)
        

class Solution2(object):
    """
    提供另一种非递归思路,讨论区DP solution,效率也是高的一匹
    Runtime: 36 ms, faster than 89.58% of Python online submissions for Combination Sum II.
    Memory Usage: 11 MB, less than 6.10% of Python online submissions for Combination Sum II.
    """
    def combinationSum2(self, candidates, target):
        candidates.sort()
        table = [None] + [set() for i in range(target)]
        for i in candidates:
            if i > target:
                break
            for j in range(target - i, 0, -1):
                table[i + j] |= {elt + (i,) for elt in table[j]}
            table[i].add((i,))
        return map(list, table[target])

总结

  本次记录到此为止,一共九题,多谢观看~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值