leetcode闯关:①--⑤

leetcode官网: 官方网站.*

1 两数之和.

① 题目

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
示例:

给定 nums = [2, 7, 11, 15], target = 9 
因为 nums[0] + nums[1] = 2 + 7 = 9 
所以返回 [0, 1]

② 思考

从一个列表里面查找两个有特定关系的数,也就是知道了一个数,求另一个对应数的位置,如果不存在对应的数就下一位。
1>用一般的遍历可以做,而且可以找出来全部的,简单,但是时间复杂度为O(n^2),会超时。
2>联想到哈希表。哈希表中一个key值对应表中一个地址,且哈希表里面的数不重复,在python语言中,就类似于一个字典dict={},一个key对应一个value,若key不在表中,就添加新的元素,若key在表中,可直接查询到value。在这个实例中,key就是对应的另一个数,value就是这个数的地址。
时间复杂度: 科普.
哈希表: 哈希表.

③ 源码

class Solution(object):    
	def twoSum(self, nums, target):
		another = {}        
		for i, n in enumerate(nums):            
			another_n = target - n            
			if another_n in another:                
				return(another[another_n], i)            
			another[n] = i        
		return None

凑巧,最近在看SECOND目标识别的源码,其中在生成体素数据时,就用到了哈希表。

2 两数相加.

① 题目

两数相加.粘贴出来太麻烦了

② 思考

链表还真是第一次接触,python里面原来真的没有这个东西。先了解一下什么是链表链表定义.
在程序中,一般都是通过一个指针访问地址,才能得到地址存放的数值。一个链表每个节点(node)上,包含两个域,第一域存放了一个数值,第二域存放了指针下一次要指向的地址,也就是说要访问一个链表的话,只能按顺序进行访问,不能打乱顺序。在c语言中,可以使用一个typedef struct XXX来定义,就像一个套娃,一个数套着一个数,python中也是用套娃实现的。

③ 答案

class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        re = ListNode(0)                # 新建ListNode
        r = re                          # 考虑为什么要把re给到r,因为在循环过程中,r永远指向re的最后一位,re是总的
        carry = 0                       # 进位
        while l1 or l2:                 # x,y中只要有一个还有值,就一直计算
            x = l1.val if l1 else 0     # 没有值就给0
            y = l2.val if l2 else 0     # 没有值就给0
            s = carry+x+y               # 这一位的和
            carry = s//10               # 进位
            r.next = ListNode(s % 10)   # 创建一个新的ListNode,专门存放余位
            r = r.next                  # 套娃,r指向了原本r里面的一层,就是代表上一层已经处理好了,指向这一位的next了
            if l1 != None:              # l1还没结束,下一位,结束了的话就留在原地
                l1 = l1.next
            if l2 != None:              # l2还没结束。下一位,结束了的话就留在原地
                l2 = l2.next
        if carry > 0:                   # 最后一位还有进位,那就多加一位
            r.next = ListNode(1)
        return re.next                  # re的第一位是0,所以不要第一位了,只要next里的部分

在pycharm进行调试时,需要自己对ListNode进行定义,这里参考了这里.

3 无重复字符的最长子串.

① 思考

这种题目的共性:从一整串数据里面拿出有一定规律的一个字串,都可以用滑动窗口法。
滑动窗口法:左边一个指针,右边一个指针,读取两个指针中间的数据,需要满足一定的规律,就找到了满足要求的字串。

② 源码

class Solution1:
    def lengthOfLongestSubstring(self, s):
        # 哈希集合,记录每个字符是否出现过
        occ = set()
        n = len(s)
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                # 左指针向右移动一格,移除一个字符,一直右移到删掉了s[rk + 1]对应的字符,就可以开始右边界右移了
                occ.remove(s[i - 1])
            while rk + 1 < n and s[rk + 1] not in occ:
                # 不断地移动右指针
                occ.add(s[rk + 1])
                rk += 1
            # 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1)
        return ans

class Solution2(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        st = {}     # 这个字典key是字符,value是字符截止到j最后出现的位置
        i, ans = 0, 0
        for j in range(len(s)):
            if s[j] in st:
                i = max(st[s[j]], i)
            ans = max(ans, j - i + 1)
            st[s[j]] = j + 1
        return ans;

class Solution3:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        last_pos = {}
        left_pos = -1
        ans = 0
        if len(s) == 0:
            return 0
        for i in range(len(s)):
            if s[i] in last_pos:
                left_pos = max(left_pos, last_pos[s[i]])
                last_pos[s[i]] = i
            else:
                last_pos[s[i]] = i
            ans = max(ans, i - left_pos)
        return ans

第一种方法,滑动窗口,用哈希表,一旦发现窗口里面有重复就从左指针开始检测重复的位置,然后移动左指针直到没有重复,记录长度。
第二种方法,使用了一个哈希表,或者说字典,记录了重复内容上一次出现的位置,直接左指针移到这个位置,节约了时间,但增加了内存。
第三种方法,自己写的,和第二种一样。

4 寻找两个正序数组的中位数.

① 思考

中位数指的是一个数组从小到大排列后,在中间位置的数,既第(m+n)/2个数,同时题目要求,时间复杂度要是O(logn),如果将两个数组合并再找中位数,时间复杂度不符合要求,存在log,应该考虑到使用二分法查找。
二分法:每次取一半直到这个一半的数为1,说明下次再取就可以取到要取的数了。如要求8个数的中位数。先取2个小的,再取1个,就取走3个了,接下来的两个数取平均,就是中位数。

② 源码

# 首先看题目要求,时间复杂度要求为log(m+n),则联想到使用二分法,使用一般遍历方法时时间复杂度为m+n
# 但是在两个数列中,二分法有变形
# 求中值点,可以方便的联想到求中间位置的数,也就是求target=(m+n)/2,或者target=(m+n)/2+1这两个位置上的数
# 开始二分,l1和l2分别拿出第k/2个数,不够的话,用min(target // 2, len(nums1)),拿出直到最后一位数,比较拿出来的数大小
# 小的那一个数列的前这么多位肯定都是属于target位置之前的,去掉,同时target也减小这么多位,因为拿出去了一部分,剩下的再拿出去的话,就要少拿这么多个
# 直到把一个数列拿空了,或者target = 1,就是下一次拿的数中的某个数了
class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        def findTargetElement(nums1, nums2, target):
            while True:
                if len(nums1) == 0:
                    return nums2[target - 1]
                if len(nums2) == 0:
                    return nums1[target - 1]
                if target == 1:
                    return min(nums2[0], nums1[0])
                pos1 = min(target // 2, len(nums1)) - 1
                pos2 = min(target // 2, len(nums2)) - 1
                if nums1[pos1] >= nums2[pos2]:
                    nums2 = nums2[pos2 + 1:]
                    target -= pos2 + 1
                else:
                    nums1 = nums1[pos1 + 1:]
                    target -= pos1 + 1

        if len(nums1) > len(nums2):
            nums1, nums2 = nums2, nums1
        len_all = len(nums1) + len(nums2)
        left = (len_all + 1) // 2
        right = (len_all + 2) // 2
        return (findTargetElement(nums1, nums2, left) + findTargetElement(nums1, nums2, right)) / 2

5 最长回文子串.

① 思考

1>使用滑动窗口法:每个窗口判断第一个点和最后一个点是否相等,再第二个与倒数第二个,依次判断是否对称回文,时间复杂度较高
2>遍历中心点,求臂长: 以每一个点为中心点,求该点的对称臂长,返回最大臂长
3>动态规划:若最长回文子串满足要求,则该字串的每一个字串都是一个回文子串,这样就符合动态规划的思想,递推或者分治。

② 源码

# 由于回文子串是一个嵌套着一个,里面的是回文子串,外面的才哟可能,因此考虑使用动态规划
# 东该规划就是递推或者分治,使用递推或者递归都可以,

class Solution(object):
    # 递归方法,反复调用地思想,从长到短
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        import numpy as np
        n = len(s)
        dp = - np.ones((n, n))
        if n == 0:
            return ""
        if n == 1:
            return s

        def is_true(lens, pos, s, dp):
            if lens == 1:
                dp[pos, pos + lens - 1] = 1
            if lens == 2:
                if s[pos] == s[pos + lens - 1]:
                    dp[pos, pos + lens - 1] = 1
            if lens > 2:
                if (is_true(lens -2, pos + 1, s, dp) == 1 and s[pos] == s[pos + lens - 1]):
                    dp[pos, pos + lens - 1] = 1
            return dp[pos, pos + lens - 1]

        for lens in range(n, 0, -1):
            for pos in range(n - lens + 1):
                if is_true(lens, pos, s, dp) == 1:
                    return s[pos:pos + lens]

        # # 递推方法,从短到长,遍历全部
        # print(dp)
        # for lens in range (n, 0, -1):
        #     for pos in range(n - lens + 1):
        #         if dp[pos, pos+lens-1] == 1:
        #             return s[pos:pos+lens]

    # def longestPalindrome(self, s: str) -> str:
    #     n = len(s)
    #     dp = [[False] * n for _ in range(n)]
    #     ans = ""
    #     # 枚举子串的长度 l+1
    #     for l in range(n):
    #         # 枚举子串的起始位置 i,这样可以通过 j=i+l 得到子串的结束位置
    #         for i in range(n):
    #             j = i + l
    #             if j >= len(s):
    #                 break
    #             if l == 0:
    #                 dp[i][j] = True
    #             elif l == 1:
    #                 dp[i][j] = (s[i] == s[j])
    #             else:
    #                 dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
    #             if dp[i][j] and l + 1 > len(ans):
    #                 ans = s[i:j+1]
    #     return ans

实验结果发现:递归的时间比递推的时间要长,但是两者都是可行的,leetcode上递归会超时。
参考:以上的题目均来自于leetcode官网官网.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值