leecode刷题笔记-数组

2023-11-10

数组题注意事项:

1. 切记while循环的循环条件一定要判断遍历长度是否越界且要判断该条件,否则就会报错,例如:
while j<len(nums2) and nums2[j]<i
这里如果改为 while nums2[j]<i and j<len(nums2) 就可能会报错,因为某次循环使j++后,如果此时j已经越界,那么这样写会导致nums2[j]<i发生越界,导致报错

数组初始化方法:

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<n:循环,内层的循环放在if中,即while i+1<n and nums[i]+1==nums[i+1]:循环。核心思想是外层循环中,当符合条件时调用内层的快指针进行循环。

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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

leecode刷题笔记-数组 的相关文章

  • 【python】冒泡法--详细讲解(python实现)

    博 主 米码收割机 技 能 C Python语言 公众号 测试开发自动化 获取源码 商业合作 荣 誉 阿里云博客专家博主 51CTO技术博主 专 注 专注主流机器人 人工智能等相关领域的开发 测试技术 python 冒泡法详解 python
  • [leetcode]刷题--关于位运算的几道题

    1 位运算的本质 其实是对二进制补码储存形式的修改 位运算常见的运算符为 lt lt 左移n个位置 算数移位 符号位不变 gt gt 右移动n个位置 采用直接丢弃末尾数字的方法 符号位不变 移位都是算数移位 按位取反 对于包括符号位在内全部
  • 动态规划 - 切钢条 (python)

    博主在读 算法导论 时 看到了切钢条的自底向上的伪代码 就分享给大家 BOTTOM UP CUT ROD p n 1 let r 0 n be a new array 2 r 0 0 3 for j 1 to n 4 q 负无穷 5 for
  • 【HDLBits 刷题 12】Circuits(8)Finite State Manchines 27-34

    目录 写在前面 Finite State Manchines 2014 q3c m2014 q6b m2014 q6c m2014 q6 2012 q2fsm 2012 q2b 2013 q2afsm 2013 q2bfsm 写在前面 HD
  • 二叉树(构造篇)

    二叉树 纲领篇 先复述一下前文总结的二叉树解题总纲 是否可以通过遍历一遍二叉树得到答案 如果可以 用一个 traverse 函数配合外部变量来实现 这叫 遍历 的思维模式 是否可以定义一个递归函数 通过子问题 子树 的答案推导出原问题的答案
  • 如何快速合并两个有序数组?

    前言 大家好 我是来自于 华为 的 程序员小熊 今天给大家带来一道与 数组 相关的题目 这道题同时也是字节 微软和亚马逊等互联网大厂的面试题 即力扣上的第 88 题 合并两个有序数组 本文主要介绍 逆向双指针 的策略来解答此题 供大家参考
  • 【双指针】---反转链表

    题目 提供单向链表的头节点head 请反转链表 并返回反转后的链表 分析 同速指针 链表上两个指针 一个先出发 另一个后出发并以相同的速度跟随 通过临时指针让双指针同步前行 思路 源码 package org example pointer
  • 【牛客网刷题】VL8-VL10 generate for语句、比较数大小、function的使用

    写在前面 本系列博客记录牛客网刷题记录 日拱一卒 功不唐捐 目录 VL8 使用generate for语句简化代码 题目描述 输入描述 输出描述 RTL 设计 testbench 设计 仿真测试 VL9 使用子模块实现三输入数的大小比较 题
  • 数据结构链表题集

    题目一 113 删除排序链表中的重复数字 二 LintCode 思路 创建哨兵位头结点 原地删除 class Solution public param head head is the head of the linked list re
  • 【HDLBits 刷题 5】Circuits(1)Combinational Logic

    目录 写在前面 Combinational Logic Basic Gates Wire GND NOR Another gate Two gates More logic gates 7420 chips Truth table Two
  • 【HDLBits 刷题 13】Buliding Larger Circuits

    目录 写在前面 Buliding Larger Circuits count1k shiftcount fsm seq fsmshift fsm fancytimer fsm onehot 写在前面 以下的解题方法不一定为最佳解决方案 有更
  • 树的一些基础概念、堆和 python中heapq模块使用简介

    树定义 树是一种数据结构 比如 目录结构 树是由n个节点组成的集合 n 0 那么是一颗空树 n gt 0 那么存在一个节点为书的根节点 其他节点可分为m个子集 每个子集又为一棵子树 关于树的一些概念 根节点 例 A 叶子节点 不能分叉的节点
  • 【HDLBits 刷题 12】Circuits(8)Finite State Manchines 27-34

    目录 写在前面 Finite State Manchines 2014 q3c m2014 q6b m2014 q6c m2014 q6 2012 q2fsm 2012 q2b 2013 q2afsm 2013 q2bfsm 写在前面 HD
  • leetcode刷题(四)——概率论与数理统计(一)

    leetcode刷题系列四 主要的内容涉及概率论和数理统计的知识 例题 算法分析 int dp 12 70 double dicesProbability int n int returnSize int i j k double f do
  • 7-22龟兔赛跑/PTA基础编程题目集

    7 22 龟兔赛跑 20分 乌龟与兔子进行赛跑 跑场是一个矩型跑道 跑道边可以随地进行休息 乌龟每分钟可以前进3米 兔子每分钟前进9米 兔子嫌乌龟跑得慢 觉得肯定能跑赢乌龟 于是 每跑10分钟回头看一下乌龟 若发现自己超过乌龟 就在路边休息
  • 【搜索和回溯】剑指 Offer 28. 对称的二叉树

    题目描述 请实现一个函数 用来判断一棵二叉树是不是对称的 如果一棵二叉树和它的镜像一样 那么它是对称的 示例 输入 root 1 2 2 3 4 4 3 输出 true 题解 运用DFS遍历 递归 求解 Definition for a b
  • 2022芯原芯片设计 笔试题分析和讨论

    2022芯原设计笔试题分析和讨论 以下仅为个人理解和分析 不保证正确 欢迎大家发表自己的想法 讨论出正确答案 企业知识题 1 1 D 芯原的主要经营模式为芯片设计平台即服务 Silicon Platform as a Service SiP
  • 输出整数m中删除n位之后的最大(小)数(保持各位顺序不变)

    这个题不知道ac没有 因为是随便看到的一个题 没办法测 如果有错误请帮我指出来 看到很多复杂度很高的暴力破解方法 有三层循环的那种 我这个复杂度应该会低一点 思路 先将结果初始化最后m n个字符 放在res中 从res的第一位开始 在它前面
  • 链表【2】

    文章目录 24 两两交换链表中的节点 题目 算法原理 代码实现 143 重排链表
  • 分治—快速选择算法

    文章目录 215 数组中的第K个最大元素 1 题目 2 算法原理 3 代码实现 LCR 159 库存管理 III

随机推荐

  • java——equals(),hashCode()重写与不重写区别

    1 总结 1 两个obj 如果equals 相等 hashCode 一定相等 2 两个obj 如果hashCode 相等 equals 不一定相等 2 不重写equals hashCode 不重写的时候 比较两个对象是否 相等 默认跟 效果
  • QT中实现当前时间实时更新

    如果是通过qt designer弄了个lcdNumber 想通过这个控件显示时间 那么你可以这么做 在 h文件中 1 添加头文件 include
  • 龙书学习笔记

    目录 第一章 引论 1 1 语言处理器 1 2 一个编译器的结构 1 2 1 词法分析 1 2 2 语法分析 1 2 3 语义分析器 1 2 4 中间代码生成
  • 计算机网络八股文

    浏览器输入一个网站后 具体发生了什么 进行DNS解析操作 根据DNS解析结果查找到服务器IP地址 通过IP寻址找到服务器 并利用三次握手建立TCP连接 浏览器生成HTTP保温 发送HTTP请求 等待服务器响应 服务器处理请求 返回服务器 根
  • 高速USB 2.0的CMSIS-DAP调试器:CMSIS-DAP正确打开方式(3月18日更新速度和稳定性)

    3月18日注 修改USB最大包长度到1024 HS支持 USB初始化前增加等待100ms CMSIS DAP Debugger 是 ARM 发布的面向 Cortex 系列 MCU 的开源 Apache 2 0协议 JTAG 与 SWD 调试
  • 解决github长期未登录,ssh keys过期的问题——git@github.com: Permission denied (publickey).

    首先声明 在我的PC端同时存在着github与gitlib的ssh keys 今天想看一个github上的项目 发现git pull的时候又permission denied了 如下图所示 处理办法很简单 只要把ssh key再加一遍就好了
  • 用于python环境下的数据操作_写给非计算机相关专业的同学——从零开始如何用python处理数据(包括如何安装环境)...

    文章目录 1 使用语言和包 1 2 pandas包的安装 这里只是一个例子 2 要做的一个数据处理 2 1 数据处理的需求 2 2 代码实现 2 2 1 思路 2 2 2 读入原来的表 2 2 3 找到速度为零的所有记录 2 2 4 找到对
  • 8、 Mac iTerm2 优化

    Mac iTerm2 优化 一 悬浮窗口 首先我们来解决第一个问题 如何在任何界面呼入呼出 iTerm2 的窗口 并且悬浮在界面的顶部 相信每个人都会有这样的使用场景 你正在全屏浏览器浏览网页 或者正在全屏编辑器写代码写文章之类的 突然想到
  • 使用ansible中的playbook

    1 Playbook 的功能 playbook 是由一个或多个 play 组成的列表 Playboot 文件使用 YAML 来写的 2 YAML 简介 YAML 是一种表达资料序列的格式 类似XML Yet Another Markup L
  • MySQL修改密码的3种方式以及启动方式

    在使用数据库时 我们也许会遇到 MySQL 需要修改密码的情况 比如密码太简单需要修改等 本节主要介绍了 3 种修改 MySQL 数据库密码的方法 使用 SET PASSWORD 命令 步骤 1 输入命令mysql u root p指定 r
  • xp无法访问查找工作组计算机,一招教你搞定XP“网上邻居”、“查看工作组计算机”打不开的情况...

    作 者 杜超 2号 ID 16058 城市 江阴 摘 要 一招教你搞定XP 网上邻居 查看工作组计算机 打不开的情况 正 文 在一些被优化过的XP系统或刚安装好的系统中 有时我们要访问局域网上的其他共享打印机或文件夹 需要用到网上邻居 可是
  • TCP协议疑难杂症全景解析

    原文地址 http blog csdn net dog250 article details 6612496 说明 1 本文以TCP的发展历程解析容易引起混淆 误会的方方面面 2 本文不会贴大量的源码 大多数是以文字形式描述 我相信文字看起
  • multiset和set,map和multimap的区别

    一 set和multiset的差异和相同 set是一个集合容器 其中所包含的元素是唯一的 集合中的元素按一定的顺序排列 元素插入过程是按排序规则插入 所以不能指定插入位置 set采用红黑树变体的数据结构实现 红黑树属于平衡二叉树 在插入操作
  • 查看GPU使用的最佳方式

    1 watch n 1 nvidia smi 最有名 没有之一 nvidia自带了一个nvidia smi的命令行工具 会显示GPU使用情况 作为监控 GPU 的工具就显得有点过于简陋了 比如 Process name 栏只显示命令行的程序
  • Redis布隆过滤器详解

    目录 一 前言 二 RedisBloom 安装与使用 三 RedisBloom 常用命令汇总 四 通过 Jedis 使用 RedisBloom 五 Redisson 封装的布隆过滤器 六 使用哪种方式的过滤器比较好 一 前言 布隆过滤器 B
  • 【数据结构与算法】时间复杂度与空间复杂度

    目录 一 前言 二 时间复杂度 1 概念 二 大O的渐进表示法 概念 总结 三 常见时间复杂度计算举例 例1 例2 例3 例4 例5 计算冒泡排序的时间复杂度 例6 二分算法的时间复杂度 例7 阶乘递归Fac的时间复杂度 例8 斐波那契递归
  • js异步提交form表单之serialize()方法及FormData对象

    serialize 和FormData对象都可将表单数据序列化 后通过ajax异步提交 但二者有实质区别 1 serialize serialize 是JQuery方法 可序列化表单值创建 URL 编码文本字符串 就是将表单数据以字符串的形
  • 浏览器的工作原理

    浏览器可以被认为是使用最广泛的软件 本文将介绍浏览器的工 作原理 我们将看到 从你在地址栏输入google com到你看到google主页过程中都发生了什么 将讨论的浏览器 今天 有五种主流浏览器 IE Firefox Safari Chr
  • java.lang.UnsatisfiedLinkError: No implementation found for

    E AndroidRuntime FATAL EXCEPTION main Process com example pimr PID 20314 java lang UnsatisfiedLinkError No implementatio
  • leecode刷题笔记-数组

    数组题注意事项 1 切记while循环的循环条件一定要判断遍历长度是否越界且要先判断该条件 否则就会报错 例如 while j