• [刷题] Leetcode算法 (2020-2-26)


    1.搜索插入位置

    题目:

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例:

    输入: [1,3,5,6], 5
    输出: 2
    
    输入: [1,3,5,6], 2
    输出: 1
    
    输入: [1,3,5,6], 7
    输出: 4
    
    输入: [1,3,5,6], 0
    输出: 0

    代码:

    class Solution:
        def searchInsert(self, nums: List[int], target: int) -> int:
            # lo表示开始指针
            lo = 0
            # hi表示结束指针
            hi = len(nums)
            # 如果列表还有值
            while lo < hi:
                # 找到中心元素的索引
                mid = (lo+hi)//2
                # 比较中心元素与target的大小,如果target更大,则target属于右边部分,这是应该移动lo到mid+1
                if nums[mid] < target: lo = mid+1
                # 反之,如果target小于中心元素,则属于左边部分,应该移动hi到mid位置
                else: hi = mid
            # 最终返回lo即是target存在或应该插入的位置
            return lo

    这是使用二分查找法来寻找应该插入的位置( 时间复杂度O(logn) )。这个源码很有学习的价值。如果如要真正插入元素,则只需要在return之前使用nums.insert(lo, target)即可。

    总结:

    # 这是非常典型的二分查找问题,使用前后指针来逐步缩小列表范围。最后得到插入的索引
    # 二分查找只能应用于有序列表(反序的话需要修改以下代码中的逻辑)。
    # 二分查找法的时间复杂度为O(logn),空间复杂度为O(1)

    2.外观数列

    题目:

    「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

    1. 1
    2. 11
    3. 21
    4. 1211
    5. 111221
    6. 312211

    1 被读作  "one 1"  ("一个一") , 即 11。
    11 被读作 "two 1s" ("两个一"), 即 21。
    21 被读作 "one 2",  "one 1" ("一个二" ,  "一个一") , 即 1211。

    给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

    注意:整数序列中的每一项将表示为一个字符串。

    示例:

    输入: 1
    输出: "1"
    解释:这是一个基本样例。
    
    输入: 4
    输出: "1211"
    解释:当 n = 3 时,序列是 "21",其中我们有 "2""1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12""11" 组合在一起,也就是 "1211"

    代码1:

    class Solution:
        def countAndSay(self, n: int) -> str:
            # 定义内部函数,输入前一个的结果,输出下一个结果,例如输入"11"输出"21"
            def say(strs):
                count = 1  # 记录相同数字的个数
                cur = 0  # 记录每轮要对比的数字
                res = ""  # 用于记录结果
                # 记录第一个字符
                cur = strs[0]
                # 遍历后面的所有数
                for i in range(1, len(strs)):
                    # 如果和cur相同
                    if strs[i] == cur:
                        # count加1
                        count += 1
                        
                    # 如果不和cur相等
                    else:
                        # 将count和cur
                        res += str(count) + cur
                        # 重新将新的数赋给cur
                        cur = strs[i]
                        # count回到1
                        count = 1
                        # 如果是最后一个数
                res += str(count) + cur
    
                return res
            # n=1时,直接返回结果
            if n <= 1:
                return "1"
            # 从n=2开始,迭代调用say()
            last = "1"
            for i in range(2,n+1):
                last = say(last)
            return last

    代码比较冗余,思想比较简单。就是逐个数相同字符的个数,然后写入结果。

    代码2:

    def countAndSay(self, n: int) -> str:
        # 导入itertools的groupby
        from itertools import groupby
        # 如果n=1,则直接返回"1"
        result = '1'
        # 如果n从2开始
        for i in range(1, n):
            # groupby将"1121"变成{1:['1','1'],2:['2'],1:['1']}
            result = ''.join([str(len(list(g))) + k for k, g in groupby(result)])
        return result

    这种方式利用了python的itertools.groupby函数。不建议在写算法题的时候使用内库。但可以学习一下groupby的用法。

    3.最大子序和

    题目:

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    代码1:(暴力)

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            if not nums:
                return 0
            # _max初始化为nums[0]
            _max = nums[0]
            # 从0开始循环,每一个元素都和后面的所有元素累加,找出其中最大值即可
            for i in range(len(nums)):
                _sum = 0
                # 开始累加
                for k in range(i,len(nums)):
                    # 每累加一个数,就和_max比较一下,如果更大,则替换_max
                    _sum += nums[k]
                    _max = _sum if _sum > _max else _max
    
            return _max

    暴力解法,时间复杂度O(n^2)。就是遍历所有的可能性,然后用一个变量几率最大值即可。空间复杂度O(1)。

    代码2:(动态规划)

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            # 开始的时候,temp记录第一个数
            temp = nums[0]
            # 第一个数是最大的数
            _max = temp
            # 从index=1的数开始循环
            for i in range(1,len(nums)):
                # 判断 [当前最大值_max、累计最大列表和temp、下一个数] 之中最大的数,并从新赋值给_max
                _max = max(temp+nums[i],nums[i],_max)
                # 如果将下一个数加进事先累积的最大长度列表,发现还更小了,则上次的累积结束,下一个数更大,从新开始累积
                if temp+nums[i] < nums[i]:
                    # temp重新开始累积和
                    temp = nums[i]
                # 如果累积了下一个数,更大,则继续累积
                else:
                    temp = temp+nums[i]
                
            # 最后返回保存最大和的_max
            return _max

    这种解法是动态规划解法,时间复杂度O(n),空间复杂度O(1)。

    更巧妙的写法:

    def maxSubArray(self, nums: List[int]) -> int:
        dp=nums[:]
        for i in range(1,len(nums)):
            dp[i]=max(dp[i-1]+nums[i],nums[i])
        return max(dp)

    使用一个列表来存储某个index下的最大值(这个最大值来自于,nums[i]、前面累积的最大值+nums[i]),然后再取所有index下的最大值。

    直接在原列表上保存最大序列和:

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            # 从1开始循环
            for i in range(1, len(nums)):
                # nums[i]用来保存i之前的最大序列和,如果nums[i-1]为负,则说明nums[i-1]+nums[i]<nums[i],所以这种情况下nums[i] += 0.也就是i之前的最大序列和就是nums[i]
                nums[i] += nums[i-1] if nums[i-1] > 0 else 0
            # 全部计算完后,nums列表已经全部是保留的每个index对应的最大序列和,取最大值即可
            return max(nums)

    代码3:(分治法)

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            n = len(nums)
            #递归终止条件
            if n == 1:
                return nums[0]
            else:
                #递归计算左半边最大子序和
                max_left = self.maxSubArray(nums[0:len(nums) // 2])
                #递归计算右半边最大子序和
                max_right = self.maxSubArray(nums[len(nums) // 2:len(nums)])
            
            #计算中间的最大子序和,从右到左计算左边的最大子序和,从左到右计算右边的最大子序和,再相加
            max_l = nums[len(nums) // 2 - 1]
            tmp = 0
            for i in range(len(nums) // 2 - 1, -1, -1):
                tmp += nums[i]
                max_l = max(tmp, max_l)
            max_r = nums[len(nums) // 2]
            tmp = 0
            for i in range(len(nums) // 2, len(nums)):
                tmp += nums[i]
                max_r = max(tmp, max_r)
            #返回三个中的最大值
            return max(max_right,max_left,max_l+max_r)

    使用DC(分治法),最大序列可能出现在左、右或中间。如下图所示:

    我们将列表分成左右两部分,最大序列只可能出现在3个部分,即左边部分、右边部分、和中间跨中心的部分。

    左右部分我们分别单独找出最大值。

    中间部分,我们从中心开始往左右方向各找一个最大序列,既然都是最大序列,那加起来也是最大序列(注意,这两个序列实际上是连续的,因为都是从中心开始)。这样就可以得到跨中心部分的最大序列和。

    最后,返回max(左边、中间、右边)。然后使用DC思想进行递归。

    ##

  • 相关阅读:
    稳扎稳打Silverlight(13) 2.0交互之鼠标事件和键盘事件
    稳扎稳打Silverlight(17) 2.0数据之详解DataGrid, 绑定数据到ListBox
    再接再厉VS 2008 sp1 + .NET 3.5 sp1(2) Entity Framework(实体框架)之详解 Linq To Entities 之一
    稳扎稳打Silverlight(8) 2.0图形之基类System.Windows.Shapes.Shape
    稳扎稳打Silverlight(11) 2.0动画之ColorAnimation, DoubleAnimation, PointAnimation, 内插关键帧动画
    稳扎稳打Silverlight(21) 2.0通信之WebRequest和WebResponse, 对指定的URI发出请求以及接收响应
    稳扎稳打Silverlight(16) 2.0数据之独立存储(Isolated Storage)
    稳扎稳打Silverlight(9) 2.0画笔之SolidColorBrush, ImageBrush, VideoBrush, LinearGradientBrush, RadialGradientBrush
    稳扎稳打Silverlight(23) 2.0通信之调用WCF的双向通信(Duplex Service)
    游戏人生Silverlight(1) 七彩俄罗斯方块[Silverlight 2.0(c#)]
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12366974.html
Copyright © 2020-2023  润新知