leecode刷题笔记-数组

数组题注意事项:

1. 切记while循环的循环条件一定要判断遍历长度是否越界且要判断该条件,否则就会报错,例如:
while j 这里如果改为 while nums2[j]

数组初始化方法:

ans = [[None] * R for _ in xrange(C)]

这里的xrange()和range()完全一样。如果R=3,C=4,这里ans就初始化为一个3*4的list,其中每个值都为[None],也就是这种形式

ans=[[None, None, None, None], 
    [None, None, None, None],
    [None, None, None, None]]

首先这里的[None]*R定义了维度,例如:
E=[None]*3
此时E为[None,None,None]。后面的for _ in xrange( C )相当于for i in xrange( C ),由于循环里i没用到所以用_来占位。这里首先生成了一个行向量,其维度,值分别为1 *R,[None]。后面的for循环,循环变量的范围是<4,所以就相当于从i=0到i=3,重复生成了四个行向量[None] * R,并堆叠在一起。也就得到了上面ans的结果。
这实际上是一种简化写法。
另有一种写法:

board = [[None for _ in range(n)] for _ in range(n)]

如果n=5,board就是一个5*5内容为none的列表:
[[None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None]]
这行代码的意思就相当于:

board=[]
for i in range(n):
    list_1=[]
    for j in range(n):
        list_1.append(None)
    board.append(list_1)

外层循环n次。每次循环中首先声明空矩阵board。之后初始化一个空的list_1,append()作用是在对象的末尾填充append()括号里的内容。这里用for循环给list_1[]填入n个None,此时list_1就是一个1*n的全为None的行向量。下一行把list_1填充进board中。重复多次就形成了n *n的值为None的方阵。
总结:这种方法都是[]里套[],一般先是内层的[]定义一个行向量。之后用一个for循环,重复定义多个行向量,最后定义成一个二维矩阵。

867.转置矩阵

class Solution(object):
    def transpose(self, A):
        R, C = len(A), len(A[0])    #R:2 C:4 这里R是取了矩阵A的行数,C取了A[0],也就是A第一行的尺寸,就相当于A的列数。
        ans = [[None] * R for _ in range(C)]

        for r, row in enumerate(A):  #第一次运行到这里时有r:0 row:[1,2,3,4]
            for c, val in enumerate(row): #第一次运行到这里时有c:0 val:1
                ans[c][r] = val
        return ans
a = Solution()                               #初始化Solution的实例对象a
print(a.transpose([[1,2,3,4],[5,6,7,8]]))    #调用a的transpose()函数

方法2:
用return zip(*A)直接解决此题。
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。
我们可以使用 list() 转换来输出列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b)     # 返回一个对象
>>> zipped
<zip object at 0x103abc288>
>>> list(zipped)  # list() 转换为列表
[(1, 4), (2, 5), (3, 6)]
>>> list(zip(a,c))              # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
 
>>> a1, a2 = zip(*zip(a,b))          # 与 zip 相反,zip(*) 可理解为解压,返回二维矩阵式
>>> list(a1)
[1, 2, 3]
>>> list(a2)
[4, 5, 6]

zip()用于二维矩阵变换(矩阵的行列互换)
比如这里我们有一个由列表描述的二维矩阵a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

 >>> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  >>> A=list(zip(*a)) #A=[(1, 4, 7), (2, 5, 8), (3, 6, 9)] 

这里将list看成元组进行解压,恰好得到“行列互换”的效果,再通过对每个元素应用list()函数,将tuple转换为list,输出。

注:enumerate() 函数
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
例如

>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))       # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

在for循环中使用enumerate()

>>>seq = ['one', 'two', 'three']
>>> for i, element in enumerate(seq):
...     print i, element
... 
0 one
1 two
2 three
class Solution:
    def majorityElement(self, nums):
        for i in set(nums):
            if nums.count(i)>len(nums)/2:
                return i
        return -1
a = Solution()
print(a.majorityElement([1,2,2,2,2,2,2,2,1,2,4,5,3,4]))

17.10. 主要元素

数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1
例如输入[1,2,2,2,2,2,2,5,6] 输出[2]

class Solution(object):    
	def majorityElement(self, nums):        
		for i in set(nums):            
			if nums.count(i)>len(nums)/2: #这里不能忘记nums. nums.count(i)表示在nums的范围内对i的频次计数。如果没有nums. 就相当于对每个数本身计数,这样每个数的count都是1,就没有意义                
			return i        
	return -1

set() 函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集、差集、并集等。比如A=[1,2,2,2,2,2,2,5,6],那么set(A)=[1,2,5,6]

1588. 所有奇数长度子数组的和

给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。

子数组 定义为原数组中的一个连续子序列。

请你返回 arr 中 所有奇数长度子数组的和 。

这里除了暴力解法(用for循环,类似简易的滑动窗口),还有一种复杂度O(N)的简便方法,是算出每个数字出现的频率
odd奇数,even偶数
对于每个元素i(数组中下标为i)来说,要构成奇数长度的子数组,即 i左边的元素个数left+i本身自己一个+右边元素的个数right=奇数即 left+right=偶数
满足a+b=偶数就只有两种情况

  1. 奇数+奇数=偶数
  2. 偶数+偶数=偶数

所以只需要求得i左边可以选择奇数长度的可能有多少种,即left_odd,同样求右边奇数right_odd,就可以求出策略1有多少种可能
就是遍历一遍所有的元素,然后查看这个元素会在多少个长度为奇数的数组中出现过。

比如题目给出的第一个测试用例 [1, 4, 2, 5, 3] 中;

1 在 3 个长度为奇数的数组中出现过:[1], [1, 4, 2], [1, 4, 2, 5, 3];所以最终的和,要加上 1 * 3;

4 在 4 个长度为奇数的数组中出现过:[4], [4, 2, 5], [1, 4, 2], [1, 4, 2, 5, 3];所以最终和,要加上 4 * 4;

2 在 5 个长度为奇数的数组中出现过:[2], [2, 5, 3], [4, 2, 5], [1, 4, 2], [1, 4, 2, 5, 3];所以最终和,要加上 5 * 2;

下面的关键就是,如何计算一个数字在多少个奇数长度的数组中出现过?

对于一个数字,它所在的数组,可以在它前面再选择 0, 1, 2, … 个数字,一共有 left = i + 1 个选择;

可以在它后面再选择 0, 1, 2, … 个数字,一共有 right = n - i 个选择。

如果在前面选择了偶数个数字,那么在后面,也必须选择偶数个数字,这样加上它自身,才构成奇数长度的数组;

如果在前面选择了奇数个数字,那么在后面,也必须选择奇数个数字,这样加上它自身,才构成奇数长度的数组;

数字前面共有 left 个选择,其中偶数个数字的选择方案有 left_even = (left + 1) / 2 个,奇数个数字的选择方案有 left_odd = left / 2 个;

数字后面共有 right 个选择,其中偶数个数字的选择方案有 right_even = (right + 1) / 2 个,奇数个数字的选择方案有 right_odd = right / 2 个;

所以,每个数字一共在 left_even * right_even + left_odd * right_odd 个奇数长度的数组中出现过。

219. 存在重复元素 II

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:

输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:

输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false

此类题采用字典结构来做,因为字典可以在遍历过程中对每个元素进行编号

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
            dct = {}
            for i in range(len(nums)):
                if nums[i] in dct and i-dct[nums[i]]<=k:
                    return True
                dct[nums[i]] = i
            return False

228.汇总区间

给定一个无重复元素的有序整数数组 nums 。

返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。

列表中的每个区间范围 [a,b] 应该按如下格式输出:

“a->b” ,如果 a != b
“a” ,如果 a == b

示例 1:

输入:nums = [0,1,2,4,5,7]
输出:[“0->2”,“4->5”,“7”]
解释:区间范围是:
[0,2] --> “0->2”
[4,5] --> “4->5”
[7,7] --> “7”
示例 2:

输入:nums = [0,2,3,4,6,8,9]
输出:[“0”,“2->4”,“6”,“8->9”]
解释:区间范围是:
[0,0] --> “0”
[2,4] --> “2->4”
[6,6] --> “6”
[8,9] --> “8->9”

此题采用快慢指针

class Solution:
    def summaryRanges(self, nums: List[int]) -> List[str]:
        A=[]
        i,n=0,len(nums)
        while i<n:
            if i+1<n and nums[i]+1==nums[i+1]:
                j=i
                while i+1<n and nums[i]+1==nums[i+1]:
                    i+=1
                A.append(str(nums[j])+'->'+str(nums[i]))
            else:
                A.append(str(nums[i]))
            i+=1
        return A

这里用两个while循环,外层是慢指针,即while i

26. 删除排序数组中的重复项

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素。
示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

不需要考虑数组中超出新长度后面的元素

此题同样采用快慢指针,i是慢指针,j是快指针。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        i=0
        for j in nums[1:]:
            if j!=nums[i]:
                i+=1
                nums[i]=j
        return i+1

189.旋转数组(常考题)

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

进阶:

尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
使用空间复杂度为 O(1) 的 原地 算法解决这个问题

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

解法
方法三:数组翻转
该方法基于如下的事实:当我们将数组的元素向右移动 kk 次后,尾部 k个元素会移动至数组头部,其余元素向后移动 k\bmod nkmodn 个位置。

该方法为数组的翻转:我们可以先将所有元素翻转,这样尾部的 k mod n 个元素就被移至数组头部,然后我们再翻转 0-kmod n−1 区间的元素和 kmodn,n−1 区间的元素即能得到最后的答案。

我们以 n=7,k=3为例进行如下展示:

操作 结果
原始数组 1 2 3 4 5 6 7
翻转所有元素 7 6 5 4 3 2 1
翻转 [0,k mod n−1] 区间的元素 5 6 7 4 3 2 1
翻转 [k mod n,n−1] 区间的元素 5 6 7 1 2 3 4
代码如下:

class Solution:
    def rotate(self, nums, k):
        k=k%len(nums)
        if len(nums)>1:
            self.reverse(nums,0,len(nums)-1)
            self.reverse(nums,0,k-1)
            self.reverse(nums,k,len(nums)-1)
        return nums

    def reverse(self,nums,s,t):
        while s<=t:
            nums[s],nums[t]=nums[t],nums[s]
            s+=1
            t-=1
        return nums

复杂度分析

时间复杂度:O(n)O(n),其中 nn 为数组的长度。每个元素被翻转两次,一共 nn 个元素,因此总时间复杂度为 O(2n)=O(n)O(2n)=O(n)。

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

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4
解题思路:
使用异或运算,将所有值进行异或
异或运算,相异为真,相同为假,所以 a^a = a
因为异或运算 满足交换律 a^ b^ a = a^ a^b = b 所以数组经过异或运算,单独的值就剩下了

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return reduce(lambda x,y:x^y,nums)

这里reduce()和lambda表达式的用法参见python语法笔记

两个数组的交集 II

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

说明:

输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。

此题考虑采用双指针遍历,但是有几个点要注意:
1.先排序再遍历
2.注意判断数组是否越界,尤其是有while()循环
3.当两个指针指向相同元素时指针怎么移动
4.循环时注意指针移动的条件
思路如下:
首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。
每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,
如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。
简单来说就是,为了把两个不等长的数组都遍历完,采取的遍历策略是两个指针,当前两个值相等就都走一步,否则哪个值大哪个指针走。因为之前两个数组都排过序了,这样就能遍历完。
代码:

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        nums3=[]
        if len(nums1)==0 or len(nums2)==0:
            return nums3
        j=0
        for i in nums1:#1 2     1 1
            while j<len(nums2) and nums2[j]<i  :
                j+=1
            if j==len(nums2):#超出范围,跳出循环
                break
            if nums2[j]==i:
                nums3.append(i)
                j+=1
            if j==len(nums2):#超出范围,跳出循环
                break
        return nums3
        

加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

此题注意思路

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:

输入:digits = [0]
输出:[1]
思路
1、从最后一个值进行开始遍历
2、如果小于9就加一返回,大于等于就置零
代码:

class Solution(object):
    def plusOne(self, digits):
        i=len(digits)-1
        while i>=0:
            if digits[i]<9:
                digits[i]+=1
                return digits 
            else:
                digits[i]=0
                # if i>0:惯性思维认为当前位置0了前一位就要进位加一,所以有这两句,但是这两步是没必要的
                #     digits[i-1]+=1 因为这里while循环每次判断当前位如果小于9就会加一,加完了直接return,所以这里再加一就相当于多加了一次
                i-=1
        digits.insert(0,1)#如果能走到这一步说明一定是[9.9]变成[1.0.0]这样连续进位形式,因为如果不是的话前面就已经return了,所以这里不用判断直接在前面插入1
        return digits

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:

必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

思路
两种解法:
一种是从头开始遍历,碰到0元素就直接del删除该元素,遍历完整个数组后按照原数组的长度在后面补0
另一种是快慢指针,快慢指针交替向前,交换值,快指针一直往前走,慢指针交换一次走一步

第一种思路代码:

class Solution:
    def moveZeroes(self, nums):
        a=len(nums)
        b=a
        i=0
        while i<a:
            if nums[i]==0:
                del nums[i]
                a-=1
                i-=1
            i+=1
        b-=len(nums)
        for _ in range(b):
            nums.append(0)
        return nums

第二种思路代码:

class Solution:
    def moveZeroes(self, nums):
        fast=0
        slow=0
        while fast<len(nums):
            if nums[fast]!=0:
                nums[fast],nums[slow]=nums[slow],nums[fast]
                slow+=1
            fast+=1
        return nums

第三种思路:快慢指针,正常时快慢指针同时每次走一步,如果慢指针指到0就让快指针一直走到第一个不为0的位置,然后交换快慢指针的值。注意这里防止越界所以fast的多处判断。

class Solution(object):
    def moveZeroes(self, nums):
        if len(nums)<=1:
            return nums
        fast=slow=0
        while fast<len(nums):
            if nums[slow]==0:
                while fast<len(nums) and nums[fast]==0:
                    fast+=1
                if fast<len(nums):#这一步是为了解决如[0,0]的情况fast走到头此时fast+1会越界
                    nums[fast],nums[slow]=nums[slow],nums[fast]
            slow+=1
            fast+=1
        return nums

两数之和

给定一个整数数组 nums 和一个整数目标值 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]
示例 3:

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

思路
想到暴力法,遍历数组中每一个x,寻找数组中是否存在target-x,暴力法中每次搜索只需要x后面的每个值即可。该方法如果要实现的话需要两个嵌套循环,空间复杂度O(1),时间复杂度O(N^2)
导致复杂度高主要在这里是否存在target-x搜索策略上。考虑采用哈希表,构建一个哈希表(在python里是字典),同样从头开始遍历,把每个已经搜索过的值放到哈希表中,后面只需要找target-x是否存在哈希表中,存在就输出。
注意:这里哈希表的键值应该是每个元素x在列表中的序号,而每个键值的label是x。比如A = [2,11,15,7],target=9,那么哈希表的一部分就是{2: 0, 11: 1, 15: 2}。为什么不是{0:2, 1:11, 2:15}?因为首先nums里的值有可能重复,比如[3,2,3],target=6。其次这里要最终输出的是符合要求的x对应的位置,所以应该用x的位置序号作为键值
代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashT=dict()#创建空字典hashT
        for i,num in enumerate(nums):
            if target-num in hashT:
                return [hashT[target-num],i]
            hashT[num]=i

36. 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:

输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true
示例 2:

输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 ‘.’ 。
给定数独永远是 9x9 形式的。
思路
一个简单的解决方案是遍历该 9 x 9 数独 三 次,以确保:

行中没有重复的数字。
列中没有重复的数字。
3 x 3 子数独内没有重复的数字。
实际上,所有这一切都可以在一次迭代中完成。

方法:一次迭代
首先,让我们来讨论下面两个问题:

如何枚举子数独?
可以使用 box_index = (row / 3) * 3 + columns / 3,其中 / 是整数除法。

如何确保行 / 列 / 子数独中没有重复项?
可以利用 value -> count 哈希映射来跟踪所有已经遇到的值。

现在,我们完成了这个算法的所有准备工作:

遍历数独。
检查看到每个单元格值是否已经在当前的行 / 列 / 子数独中出现过:
如果出现重复,返回 false。
如果没有,则保留此值以进行进一步跟踪。
返回 true。

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rows=[{} for i in range(9)] #这里是初始化一个包含九个空字典的列表
        cols=[{} for i in range(9)]
        blocks=[{} for i in range(9)]
        for i in range(9):
            for j in range(9):
                if board[i][j] !=".":
                    num=int(board[i][j])
                    rows[i][num]=rows[i].get(num,0)+1 #通过get()取值后把值赋给字典rows[i]里键为num的单元对应的value
                    cols[j][num]=cols[j].get(num,0)+1
                    block_num=(i//3)*3+j//3
                    blocks[block_num][num]=blocks[block_num].get(num,0)+1
                    if rows[i][num]>1 or cols[j][num]>1 or blocks[block_num][num]>1:
                        return False
        return True


字典中get()函数作用

以classCount.get(voteIlabel,0)为例:
classCount.get(voteIlabel,0)返回字典classCount中voteIlabel元素对应的值,若无,则进行初始化

若不存在voteIlabel,则字典classCount中生成voteIlabel元素,并使其对应的数字为0,即
classCount = {voteIlabel:0}
此时classCount.get(voteIlabel,0)作用是检测并生成新元素,括号中的0只用作初始化,之后再无作用

当字典中有voteIlabel元素时,classCount.get(voteIlabel,0)作用是返回该元素对应的值,即0

以书中代码为例:
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1;

初始化classCount = {}时,此时输入classCount,输出为:
classCount = {}

当第一次遇到新的label时,将新的label添加到字典classCount,并初始化其对应数值为0
然后+1,即该label已经出现过一次,此时输入classCount,输出为:

classCount = {voteIlabel:1}

当第二次遇到同一个label时,classCount.get(voteIlabel,0)返回对应的数值(此时括号内的0不起作用,因为已经初始化过了),然后+1,此时输入classCount,输出为:

classCount = {voteIlabel:2}

可以看出,+1是每一次都会起作用的,因为不管遇到字典内已经存在的或者不存在的,都需要把这个元素记录下来

48. 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
示例 3:

输入:matrix = [[1]]
输出:[[1]]
示例 4:

输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]

思路:先转置后镜像对称

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        row,col=len(matrix),len(matrix[0])
        if row==1 and col==1:
            return matrix
        for i in range(row-1):
            for j in range(i+1,col):
                matrix[i][j],matrix[j][i]=matrix[j][i],matrix[i][j]
        for i in range(row):
                matrix[i]=matrix[i][::-1]
        return matrix

注意:这里有一个问题是第一步转置的时候如果对整个矩阵都做一遍[i][j]=[j][i],就等于对整个矩阵做了两次转置,等于没改变。所以应该只对对角线一侧的一半元素进行操作。所以图中对于第i行,只操作第i+1:第col个元素。还有注意这里镜像翻转的方法:
a=[1,2,3.4,5]
print(a[::-1]) ### 取从后向前(相反)的元素
[ 5 4 3 2 1 ]

15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

「不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:

第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;

第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。

也就是说,我们枚举的三元组 (a, b, c)满足 a≤b≤c,保证了只有 (a, b, c)这个顺序会被枚举到,而 (b, a, c)、(c, b, a) 等等这些不会,这样就减少了重复。重复的三元组指同一个元素取两次,就是重复的三元组。

暴力算法是三重循环 时间复杂度O(N^3)很容易想到采用双指针,要实现这一点,我们可以将数组中的元素从小到大进行排序,随后将三重循环的第三重循环变成一个从数组最右端开始向左移动的指针,前两种变成双指针即可,分别从数组左右向中心移动。

什么时候可以用双指针?
当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从0(N2)减少至O(N),为什么是O(N)呢?这是因为在枚举的过程每一步中, 「左指针」会向右移动一个位置(也就是题目中的b) ,而[右指针」会向左移动若干个位置,这个与数组的元素有关,但我们知道它一共会移动的位置数为O(N),均摊下来,每次也向左移动一个位置,因此时间复杂度为O(N)。

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        ans=[]
        tmpans=[]
        n=len(nums)
        if n<3:
            return None
        nums.sort()
        i=0
        while i<n-2:
            if i>0 and nums[i]==nums[i-1]:
                i+=1
                continue
            j=i+1
            while nums[j]==nums[j-1]:
                j+=1
            k=n-1
            while k<n-1 and nums[k]==nums[k+1]:
                k-=1
            while j!=k:
                sum=-(nums[j]+nums[k])
                if nums[i]==sum:
                    tmpans.append(nums[i],nums[j],nums[k])
                    ans.append(tmpans)
                elif nums[i]<sum:
                    k-=1
                else:
                    j+=1
            i+=1
        return ans

你可能感兴趣的:(刷题)