数据结构学习计划_入门_第三天_数组

这篇博客介绍了如何利用哈希表、排序和双指针解决两个数组交集的问题,并提供了多种解决方案。此外,还探讨了动态规划在买卖股票最佳时机问题中的应用,展示了一次遍历和动态规划方法。这些算法在时间和空间复杂度上进行了优化,确保了高效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

350.两个数组的交集 II

https://leetcode.cn/problems/intersection-of-two-arrays-ii/
数组 哈希表 双指针 二分查找

给你两个整数数组nums1nums2,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

我的思路:首先考虑哈希表,然后可以考虑排序和双指针

方法一:哈希表

1、用哈希表(字典)实现collections.Counter()的功能,即返回键值为数组元素,值为数组元素个数的字典。
2、首先遍历nums1数组,即较短的数组,可以降低空间复杂度。定义一个字典count=dict()记录nums1数组中的元素与元素个数。
3、遍历nums2数组,判断nums2数组中的元素是否在count中并且该元素个数不为0,如果是则将该元素记录到结果数组中,然后count中该元素的个数减1

from collections import Counter

class Solution:
	def intersect(self, nums1: List[int], nums: List[int]) -> List[int]:
		# 降低空间复杂度,先遍历较短的数组,再遍历较长的数组得到交集
		if len(nums1) > len(nums2):
			return intersect(nums2, nums1)
		# count = Counter(nums1)
		count = {}
		# 自己试着实现Counter的功能
		for num in nums1:
			if num in count:
				count[num] += 1
			else:
				count[num] = 1
		ans = []
		for num in nums2:
			if num in count and count[num]:
				ans.append(num)
				count[num] -= 1
		return ans

方法二:利用Counter方法

import collections

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        num1 = collections.Counter(nums1)
        num2 = collections.Counter(nums2)
        num = num1 & num2   # &求两个字典中key的交集
        return list(num.elements())  # Counter对象elements()方法

方法三:排序+双指针

如果两个数组是有序的,则可以使用双指针的方法得到两个数组的交集。首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        p1, p2 = 0, 0
        ans = []
        while p1 < len(nums1) and p2 < len(nums2):
            if nums1[p1] < nums2[p2]:
                p1 += 1
            elif nums1[p1] == nums2[p2]:
                ans.append(nums1[p1])
                p1 += 1
                p2 += 1
            else:
                p2 += 1
        return ans

推荐方法一,占用空间较少。

拓展:349.两个数组的交集

相当于350求交集需要去重

给定两个数组 nums1 和 nums2 ,返回它们的交集 。输出结果中的每个元素一定是唯一的。我们可以 不考虑输出结果的顺序 。
我的思路:1、字典记录数组元素的值和个数,然后判断两个字典的键值是否有相同;2、可以用set实现。

方法一:字典实现(哈希表)

# 运行时间很长,可能复杂度比较高
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        def Counter(nums):
            count = {}
            for num in nums:
                if num in count:
                    count[num] += 1
                else:
                    count[num] = 1
            return count
        # 去重
        count1 = Counter(nums1)
        count2 = Counter(nums2)
        ans = count1.keys() & count2.keys()  # 得到的是一个集合set
        return list(ans)

方法二:用set实现:(官方解法一:两个集合)

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 先遍历短的数组,降低复杂度
        if len(nums1) > len(nums2):
            return self.intersection(nums2, nums1)
        set1 = set(nums1)
        set2 = set(nums2)
        return [x for x in set1 if x in set2]
        # ans = set1 & set2
        # return list(ans)

复杂度分析
时间复杂度:O(m+n)O(m+n)O(m+n),其中 m 和 n 分别是两个数组的长度。使用两个集合分别存储两个数组中的元素需要 O(m+n)O(m+n)O(m+n) 的时间,遍历较小的集合并判断元素是否在另一个集合中需要 O(min⁡(m,n))O(\min(m,n))O(min(m,n)) 的时间,因此总时间复杂度是 O(m+n)O(m+n)O(m+n)
空间复杂度:O(m+n)O(m+n)O(m+n),其中 m 和 n 分别是两个数组的长度。空间复杂度主要取决于两个集合。

方法三:排序+双指针(官方解法二)

加入答案的数组的元素一定是递增的,为了保证加入元素的唯一性,我们需要额外记录变量 preprepre 表示上一次加入答案数组的元素。
如果两个数字相等,且该数字不等于 preprepre ,将该数字添加到答案并更新变量 preprepre ,同时将两个指针都右移一位。……
变量 preprepre 可以用 ans[−1]ans[-1]ans[1] 来表示,添加元素时自动更新。
重点:保证唯一性:if not ans or nums[p1] != ans[-1]:

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        ans = []
        p1, p2 = 0, 0
        while p1 < len(nums1) and p2 < len(nums2):
            if nums1[p1] < nums2[p2]:
                p1 += 1
            elif nums1[p1] == nums2[p2]:
                # 保证加入元素的唯一性!
                if not ans or nums1[p1] != ans[-1]:
                    ans.append(nums1[p1])
                p1 += 1          
                p2 += 1
            else:
                p2 += 1
        return ans

补充:求交集,差集,并集。
在python3环境下,字典的keys()或者items()可以求交集、差集和并集,values()不可以。如果相对values()方法进行集合操作,必须先将值转化为集合。set(a.values())

a = {'x' : 1, 'y' : 2, 'z' : 3}
b = {'w' : 10, 'x' : 11, 'y' : 2}
 
print('Common keys:', a.keys() & b.keys())           # & 求交集
print('Keys in a not in b:', a.keys() - b.keys())    # - 求差集
print('Keys in a or in b',a.keys() | b.keys())       # | 求并集
 
print('(key,value) pairs in common:', a.items() & b.items())
print('(key,value) pairs in a not in b:', a.items() - b.items())
print('(key,value) pairs in a or in b:', a.items() | b.items())


121. 买卖股票的最佳时机

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
数组 动态规划

给定一个数组prices,它的第i个元素prices[i]表示一支给定股票第i天的价格。你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

方法一:一次遍历(可以理解为动态规划的优化)

思路(官方题解方法二:一次遍历)
遍历一遍数组,计算每次到当天为止的最小股票价格和最大利润。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 一次遍历(可以理解为动态规划的优化)
        minPrice = float("inf")  # 定义一个极大值
        # minprice = int(1e9)
        maxProfit = 0
        for price in prices:
            minPrice = min(price, minPrice)
            maxProfit = max(price - minPrice, maxProfit)
        return maxProfit

复杂度分析
时间复杂度:O(n)\mathcal{O}(n)O(n),遍历了一遍数组。
空间复杂度:O(1)\mathcal{O}(1)O(1),使用了有限的变量。

方法二:动态规划

动态规划一般分为一维、二维、多维(使用状态压缩),对应形式为dp(i)dp(i)dp(i)dp(i)(j)dp(i)(j)dp(i)(j)、二进制dp(i)(j)dp(i)(j)dp(i)(j)

1. 动态规划做题步骤

  • 明确 dp(i)dp(i)dp(i) 应该表示什么(二维情况:dp(i)(j)dp(i)(j)dp(i)(j));
  • 根据 dp(i)dp(i)dp(i)dp(i−1)dp(i-1)dp(i1) 的关系得出状态转移方程;
  • 确定初始条件,如 dp(0)dp(0)dp(0)

2. 本题思路

其实方法一的思路不是凭空想象的,而是由动态规划的思想演变而来。这里介绍一维动态规划思想

dp[i]dp[i]dp[i] 表示前 iii 天的最大利润,因为我们始终要使利润最大化,则:
dp[i]=max(dp[i−1],prices[i]−minprice)dp[i] = max(dp[i-1], prices[i]-minprice)dp[i]=max(dp[i1],prices[i]minprice)

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 一维动态规划思想
        n = len(prices)
        if n == 0: return 0   # 边界条件

        # 1、初始化dp[0]
        dp = [0] * n
        dp[0] = 0
        minPrice = prices[0]

        # 2、递推公式
        for i in range(1, n):
            minPrice = min(minPrice, prices[i])
            dp[i] = max(dp[i - 1], prices[i] - minPrice)
        return dp[-1]   # 等价于 return dp[n - 1]
        # 返回 dp数组的最后一项;如果直接取 dp(n)是越界的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pythonistas29xs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值