数组_总结

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

217.存在重复元素

https://leetcode.cn/problems/contains-duplicate/
数组 哈希表 排序
给你一个整数数组 nums 。如果任一值在数组中出现至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

方法一:dict()

思路:快递检索数据,用哈希表。python可以用字典来实现哈希表。
循环遍历数组,并将数组元素存储在字典中,判断是否存在相同元素,存在则返回True;若循环结束,不存在相同元素,则返回False;

from typing import List

class Solution:
	def containsDuplicate(self, nums: List[int]) -> bool:
		record = {}     # records = dict()    {1:0,3:1,4:2}
		for idx, val in enumerate(nums):   
			if val not in record:     #  指的是键值,相当于records.keys()
				record[val] = idx
			else:
				return True
		return False
		
if __name__ == '__main__':
    nums = [1,3,4,3,2,4,2]
    so = Solution()
    print(so.containsDuplicate(nums))

用判断值的方法,为什么会超出时间限制?

class Solution:
   def containsDuplicate(self, nums: List[int]) -> bool:
   	record = {}     
   	for idx, val in enumerate(nums):   
   		if val not in record.values():
   			record[idx] = val 
   		else:
   			return True
   	return False

方法二:set()

set() 函数创建一个无序不重复元素集

class Solution:
	def containsDuplicate(self, nums: List[int]) -> bool:
		record = set()     
		for num in nums:   
			if num in record:
				return True
			else:
				record.add(num)
		return False

方法三:暴力排序

class Solution:
	def containsDuplicate(self, nums: List[int]) -> bool:
		nums.sort()
		for i in range(len(nums) - 1):
			if nums[i] == nums[i+1]:
				return True
		return False

53.最大子数组和

https://leetcode.cn/problems/maximum-subarray/
数组 分治 动态规划
给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组中的一个连续部分。

方法一:动态规划

  • 初始化: d p [ 0 ] = n u m s [ 0 ] dp[0] = nums[0] dp[0]=nums[0]
  • 取当前元素之前的和的最大值: p r e S u m = m a x ( p r e S u m + n u m s [ i ] , n u m s [ i ] ) preSum = max(preSum+nums[i], nums[i]) preSum=max(preSum+nums[i],nums[i])
  • 状态转移方程:
    d p [ i ] = m a x ( d p [ i − 1 ] , p r e S u m ) dp[i] = max(dp[i - 1], preSum) dp[i]=max(dp[i1],preSum)
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # 动态规划思想
        # 初始化
        dp = [0] * len(nums)
        dp[0] = nums[0]    # 考虑len(nums)为1时,值为nums[0]
        preSum = nums[0]

        # 递推公式
        for i in range(1, len(nums)):
            preSum = max(preSum + nums[i], nums[i])
            dp[i] = max(dp[i - 1], preSum)
        return dp[-1]   # 等价于dp[len(nums)-1]

方法二:动态规划_优化

创建一个数组用来存储子数组的和,取最大值。
dp[i-1]是到前一项为止的最大子序和,如果dp[i-1]大于0,则下一个子数组和是dp[i]和dp[i-1]相加,否则就是dp[i]本身。

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

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

1.两数之和

https://leetcode.cn/problems/two-sum/
数组 哈希表

给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

方法一:字典实现哈希表

返回下标

class Solution:
	def twoSum(self, nums: List[int], target: int) -> List[int]:
		record = {}
		for idx, val in enumerate(nums):
			if target - val not in record:
				record[val] = idx
			else:
				return [record[target - val], idx]

返回值

class Solution:
	def twoSum(self, nums: List[int], target: int) -> List[int]:
		record = {}
		for idx, val in enumerate(nums):
			if target - val not in record:
				record[val] = idx
			else:
				return [target - val, val]
复杂度分析:

时间复杂度: O ( N ) O(N) O(N),其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O ( 1 ) O(1) O(1) 地寻找 target - x。
空间复杂度: O ( N ) O(N) O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。

方法二:暴力

class Solution(object):
    def twoSum(self, nums, target):
        n = len(nums)
        for i in range(n):
            for j in range(i+1,n):
                if nums[i]+nums[j]==target:
                    return i,j
复杂度分析:

时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 NN 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。

空间复杂度: O ( 1 ) O(1) O(1)

88.合并两个有序数组

https://leetcode.cn/problems/merge-sorted-array/
数组 双指针 排序

给你两个按非递减顺序排列的整数数组nums1nums2,另有两个整数mn,分别表示nums1nums2中的元素数目。
请你合并nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组nums1中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

题目分析:不同于合并有序链表,题目中说明要直接在nums1上修改,因此不能开辟一个新的空数组不断append那种经典的做法。

方法一:直接合并后排序

最直观的方法是先将数组 nums 2 \textit{nums}_2 nums2放进数组 nums 1 \textit{nums}_1 nums1的尾部,然后直接对整个数组进行排序。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        nums1[m:] = nums2
        nums1.sort()
复杂度分析

时间复杂度: O ( ( m + n ) log ⁡ ( m + n ) ) O((m+n)\log(m+n)) O((m+n)log(m+n))
排序序列长度为 m + n m+n m+n,套用快速排序的时间复杂度即可,平均情况为 O ( ( m + n ) log ⁡ ( m + n ) ) O((m+n)\log(m+n)) O((m+n)log(m+n))
空间复杂度: O ( log ⁡ ( m + n ) ) O(\log(m+n)) O(log(m+n))
排序序列长度为 m + n m+n m+n,套用快速排序的空间复杂度即可,平均情况为 O ( log ⁡ ( m + n ) ) O(\log(m+n)) O(log(m+n))

方法二:双指针

思路:利用数组 nums 1 \textit{nums}_1 nums1 nums 2 \textit{nums}_2 nums2已经被排序的性质。
为了利用这一性质,我们可以使用双指针方法。这一方法将两个数组看作队列,每次从两个数组头部取出比较小的数字放到结果中。为两个数组分别设置一个指针 p 1 p_1 p1 p 2 p_2 p2来作为队列的头部指针。

tips:除了对比大小,还要考虑到p1=m和p2=n的情况,分情况讨论。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        # 双指针
        sorted = []
        p1 , p2 = 0, 0
        while p1 < m or p2 < n:
            if p1 == m:
                sorted.append(nums2[p2])
                p2 += 1
            elif p2 == n:
                sorted.append(nums1[p1])
                p1 += 1
            elif nums1[p1] < nums2[p2]:
                sorted.append(nums1[p1])
                p1 += 1
            else:
                sorted.append(nums2[p2])
                p2 += 1
        nums1[:] = sorted

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

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 分别是两个数组的长度。空间复杂度主要取决于两个集合。

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

加入答案的数组的元素一定是递增的,为了保证加入元素的唯一性,我们需要额外记录变量 p r e pre pre 表示上一次加入答案数组的元素。
如果两个数字相等,且该数字不等于 p r e pre pre ,将该数字添加到答案并更新变量 p r e pre pre ,同时将两个指针都右移一位。……
变量 p r e pre pre 可以用 a n s [ − 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),使用了有限的变量。

方法二:动态规划

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

1. 动态规划做题步骤

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

2. 本题思路

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

d p [ i ] dp[i] dp[i] 表示前 i i i 天的最大利润,因为我们始终要使利润最大化,则:
d p [ i ] = m a x ( d p [ i − 1 ] , p r i c e s [ i ] − m i n p r i c e ) 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)是越界的

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

27.移除元素

https://leetcode.cn/problems/remove-element/
数组 双指针

给你一个数组num和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

快慢指针法:定义两个指针,快指针 fast \textit{fast} fast 指向当前将要处理的元素,慢指针 slow \textit{slow} slow 指向下一个将要赋值的位置。

  • 如果快指针指向的元素不等于 val \textit{val} val,它一定是输出数组的一个元素,我们就将快指针指向的元素复制到慢指针位置,然后将快慢指针同时右移;

  • 如果快指针指向的元素等于 val \textit{val} val,它不能在输出数组里,此时慢指针不动,快指针右移一位。

class Solution():
	def removeElement(self, nums: List[int], val: int) -> int:
		n = len(nums)
		if n == 0: return 0
		slow, fast = 0, 0
		while slow < n and fast < n:   # 其实只要fast < n就行了
			if nums[fast] != val:
				nums[slow] = nums[fast]
				slow += 1
			fast += 1
		return slow 

复杂度分析
时间复杂度: O ( n ) O(n) O(n),其中 n 为序列的长度。我们只需要遍历该序列至多两次。
空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常数的空间保存若干变量。


209.长度最小的数组

https://leetcode.cn/problems/minimum-size-subarray-sum/
数组 滑动窗口

给定一个含有n个正整数的数组和一个正整数target
找出该数组中满足其和≥ target的长度最小的连续子数组 [ n u m s l , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r] [numsl,numsl+1,...,numsr1,numsr],并返回其长度。如果不存在符合条件的子数组,返回 0 。

方法一:暴力法

需要思考,双层循环,求长度最小。

方法二:滑动窗口法(双指针)

滑动窗口法也可以理解为双指针,两个指针为滑动窗口的起始位置和终止位置,
实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?

  • 如何移动窗口的起始位置?

  • 如何移动窗口的结束位置?

  • 窗口就是满足其和 ≥ s的长度最小的连续子数组。

  • 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

  • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
    解题的关键在于窗口的起始位置如何移动。

每一轮迭代,将 nums [ e n d ] \text{nums}[end] nums[end] 加到 sum \textit{sum} sum,如果 sum ≥ s \textit{sum} \ge s sums,则更新子数组的最小长度(此时子数组的长度是 end − start + 1 \textit{end}-\textit{start}+1 endstart+1),然后将 nums [ s t a r t ] \text{nums}[start] nums[start] sum \textit{sum} sum 中减去并将 start \textit{start} start 右移,直到 sum < s \textit{sum} < s sum<s,在此过程中同样更新子数组的最小长度。在每一轮迭代的最后,将 end \textit{end} end 右移。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 滑动窗口法(双指针)
        res = float("inf")
        Sum = 0
        index = 0   # 滑动窗口的起始位置
        for i in range(len(nums)):   # 滑动窗口的终止位置
            Sum += nums[i]
            while Sum >= target:
                res = min(res, i - index + 1)
                Sum -= nums[index]
                index += 1
        return 0 if res == float("inf") else res

拓展:

给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和=target的长度最小的连续子数组。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 滑动窗口法(双指针)
        res = float("inf")
        Sum = 0
        index = 0   # 滑动窗口的起始位置
        for i in range(len(nums)):   # 滑动窗口的终止位置
            Sum += nums[i]
            while Sum > target:  # 大于target时向前滑动
                Sum -= nums[index]
                index += 1
            while Sum == target:  # 等于tartget时,记录子数组长度
                res = min(res, i - index + 1)
                Sum -= nums[index]
                index += 1
        return 0 if res == float("inf") else res

566.重塑矩阵

https://leetcode.cn/problems/reshape-the-matrix/
数组 矩阵 模拟
可以直接从二维数组nums 得到 r 行 c 列的重塑矩阵:

  • 设 nums 本身为 m 行 n 列,如果 m n ≠ r c mn \neq rc mn=rc,那么二者包含的元素个数不相同,因此无法进行重塑;
  • 否则,对于 x ∈ [ 0 , m n ) x \in [0, mn) x[0,mn),第 x 个元素在 nums 中对应的下标为 ( x   /   n , x   %   n ) (x ~/~ n, x~\%~ n) (x / n,x % n),而在新的重塑矩阵中对应的下标为 ( x   /   c , x   %   c ) (x ~/~ c, x~\%~ c) (x / c,x % c)。我们直接进行赋值即可。
    重点:对于 x ∈ [ 0 , m n ) x \in [0, mn) x[0,mn),第 x 个元素在 nums 中对应的下标为 ( x   /   n , x   %   n ) (x ~/~ n, x~\%~ n) (x / n,x % n),而在新的重塑矩阵中对应的下标为 ( x   /   c , x   %   c ) (x ~/~ c, x~\%~ c) (x / c,x % c)
class Solution:
    def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
        m, n = len(mat), len(mat[0])
        if m * n != r * c:
            return mat
        
        ans = [[0] * c for _ in range(r)]
        for x in range(m * n):
            ans[x // c][x % c] = mat[x // n][x % n]
        return ans

118.杨辉三角

https://leetcode.cn/problems/pascals-triangle/
数组 动态规划

给定一个非负整数numRows,生成「杨辉三角」的前numRows行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。

每个数字等于上一行的左右两个数字之和,可用此性质写出整个杨辉三角。即第 n行的第 i 个数等于第 n-1 行的第 i-1 个数和第 i 个数之和。这也是组合数的性质之一,即 C n i = C n − 1 i + C n − 1 i − 1 \mathcal{C}_n^i=\mathcal{C}_{n-1}^i+\mathcal{C}_{n-1}^{i-1} Cni=Cn1i+Cn1i1

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        res = []
        for i in range(numRows):
            row = []
            for j in range(0, i+1):  # 第i行
                if j == 0 or j == i:
                    row.append(1)  # 第i行的第一个数和最后一个数为1
                else:  # 第i-1行的第j-1个数和第j个数之和
                    row.append(res[i-1][j-1] + res[i-1][j])
            res.append(row)
        return res

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

数组 哈希表 矩阵

36.有效的数独

https://leetcode.cn/problems/valid-sudoku/
数组 哈希表 矩阵

请你判断一个 9 x 9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
1、数字 1-9 在每一行只能出现一次。
2、数字 1-9 在每一列只能出现一次。
3、数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

**哈希表是怎么记录的:**遍历数独,如果出现了数字num,则创建的哈希表中在num - 1的索引位置加1,代表这个数字出现过一次。
注:定义9个3x3的三维数组,注意数组的索引范围,subBoxs[x][y][z],x可以取0到8,y可以取值0,1,2,z可以取值0,1,2。

三维数组被绕晕了
用二维数组表示3x3

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rows = [[0] * 9 for _ in range(9)]
        cols = [[0] * 9 for _ in range(9)]
        subBoxs = [[0] * 9 for _ in range(9)]
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j]) - 1
                    index = (i // 3) * 3 + j // 3
                    rows[i][num] += 1
                    cols[j][num] += 1
                    subBoxs[index][num] += 1
                    if rows[i][num] > 1 or cols[j][num] > 1 or subBoxs[index][num] > 1:
                        return False
        return True
class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rows = [[0] * 9 for _ in range(9)]
        cols = [[0] * 9 for _ in range(9)]
        subBoxs = [[0] * 9 for _ in range(9)]
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j]) - 1
                    index = (i // 3) * 3 + j // 3
                    if rows[i][num] or cols[j][num] or subBoxs[index][num]:
                        return False
                    rows[i][num] = cols[j][num] = subBoxs[index][num] = 1
        return True

73.矩阵置零

参考官方解法

方法一:使用标记数组

思路和算法:
我们可以用两个标记数组分别记录每一行和每一列是否有零出现。
具体地,我们首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为 true \text{true} true。最后我们再次遍历该数组,用标记数组更新原数组即可。

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m, n = len(matrix), len(matrix[0])
        row, col = [False] * m, [False] * n

        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    row[i] = col[j] = True

        for i in range(m):
            for j in range(n):
                if row[i] or col[j]:
                    matrix[i][j] = 0

在这里插入图片描述

方法二:使用两个标记变量

在这里插入图片描述

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        m, n = len(matrix), len(matrix[0])
        flag_col0 = any(matrix[i][0] == 0 for i in range(m))
        flag_row0 = any(matrix[0][j] == 0 for j in range(n))
        
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = matrix[0][j] = 0
        
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
        
        if flag_col0:
            for i in range(m):
                matrix[i][0] = 0
        
        if flag_row0:
            for j in range(n):
                matrix[0][j] = 0

在这里插入图片描述

方法三:使用一个标记变量

在这里插入图片描述

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        m, n = len(matrix), len(matrix[0])
        flag_col0 = False
        
        for i in range(m):
            if matrix[i][0] == 0:
                flag_col0 = True
            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = matrix[0][j] = 0
        
        for i in range(m - 1, -1, -1):
            for j in range(1, n):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
            if flag_col0:
                matrix[i][0] = 0

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Pythonistas29xs

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

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

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

打赏作者

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

抵扣说明:

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

余额充值