数组

年前刷过的题已经有些忘了,建议大家刷题每天要固定时间,定量的刷效率才高点,不然三天打鱼两天晒网就会像我现在这样。。。

1.实现二维数组的查找(easy)

题目: 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数

思路:
从二维数组的右上角的元素开始判断,因为此元素是它所在行的最大数,是它所在的列的最小数。
如果它等于要查找的数字,则查找过程结束。
如果它大于要查找的数字,则可以排除它所在的列,说明在左边,列-1
如果它小于要查找的数字,则可排除它所在的行,说明下方,行+1

def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix:
            return False
        row = len(matrix)
        col = len(matrix[0])-1
        i=0
        while  i<row and col>=0:
                n = matrix[i][col]
                if n==target:
                    return True
                elif n>target:
                    col-=1
                else:
                    i+=1
        return False
2.寻找数组的中心索引(easy)

题目:给定一个整数类型的数组 nums,请编写一个能够返回数组“中心索引”的方法。定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,返回 -1。如果数组有多个中心索引,返回最靠近左边的那一个

思路:依次遍历求左边的和,当索引左边的和等于右边的和,即左边的和=(总和-当前索引的值)/2,则为中心索引

 def pivotIndex(self, nums: List[int]) -> int:
        total = sum(nums)
        p = 0
        for i,j in enumerate (nums):
            if p == (total-j)/2:
                return i
            p += j
        return -1
3.和为K的子数组(medium)

题目:给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

思路:1)暴力法,先确定一个数,然后依次遍历求和其与剩下的数,即遍历所有可能的子数组,并对每个子数组求和,用count计算个数

 def subarraySum(self, nums: List[int], k: int) -> int:
        count=0
        for i in range(len(nums)):
            total=0
            for j in range(i,len(nums)):#注意,从i开始,不是i+1(会遗漏k=nums[i+1]的情况)
                total+=nums[j] 
                if total==k:
                    count+=1
        return count

2)利用哈希表存储total 以及其出现的次数,如果total-k在哈希表存在,则count加上其索引(出现的次数,类似求两数和,下一道题就是了),并且检查当前total是否存在哈希表内,没有则添加,有则次数再加一

def subarraySum(self, nums: List[int], k: int) -> int:
        count=0
        total=0
        hash ={0:1}
        for i in range(len(nums)):
            total+=nums[i]
            if total-k in hash:
                count+=hash[total-k]
            if total in hash:
                hash[total]+=1
            else:
                hash[total]=1
        return count
4.两数之和(easy)

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

思路:1)暴力法:从前往后遍历,将 索引 i 与其之后的值相加是否等于target

def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            m = target-nums[i]
            for j in range(i+1,len(nums)):
                if m == nums[j]:
                    return [i,j]

2)哈希法,先把nums的索引和值存入哈希表,再遍历数组,a=target-num[i],求对应的哈希表中对应的索引值h[a]。升级:两者合并,可以边查询边存入

 def twoSum(self, nums: List[int], target: int) -> List[int]:
        res = {}
        for index,v in enumerate (nums):
            if target-v in res: #查询
                return [index,res[target-v]]
            res[v]=index  #存入
      
5.三数之和(medium)

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

思路:双指针+排序
1)先对数组排序,固定一个起始数,设置left和right指针分别指向n+1和最后一个数,通过这三数的和来判断指针的去向。
当nums[n] + nums[left] +nums[right]=0, 则添加这三个元素,left+=1,right-=1
当nums[n] + nums[left] +nums[right]>0,说明数字太大,right-=1
当nums[n] + nums[left] +nums[right]<0, left+=1
2)考虑出现重复数字的情况
三种情况:起始数,left和right所指向的数。因为数字重复直接跳过本次循环操作,进入下一个数字
3)考虑特殊情况,加快运行速度
以下两种情况直接返回 [ ]
a.数组长度小于3
b.数组长度大于3 并且起始数就大于0(排序过,第一个数大于0,再加上剩下的肯定大于0)

def threeSum(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        nums = sorted(nums)
        res=[]
        if not nums or n<3:
            return res
        for i in range(n):
            if i>0 and nums[i]==nums[i-1]:  #考虑起始数重复情况
                continue
            left =i+1
            right= n-1
            target = 0-nums[i]
            if nums[i]>0:
                return res
            while left<right:
                if nums[left]+nums[right]==target:  
                    res.append([nums[i],nums[left],nums[right]])
                    while left<right and nums[left+1]==nums[left]: #考虑left重复情况
                        left+=1
                    while left<right and nums[right]==nums[right-1]:#考虑right重复情况
                        right-=1
                    right-=1
                    left+=1
                elif nums[left]+nums[right]>target:
                    right-=1
                else:
                    left+=1
        return res
6.四数之和(medium)

题目: 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

思路:与求三数之和相似,还是采用双指针,多了一层循环。
注意在考虑特殊情况时,从四个元素的极大值和极小值思考过滤重复计算
1)针对起始数–极大值情况:[i]+nums[n-1]+nums[n-2]+nums[n-3]<target,说明最大的三个数和最小的初始数相加还小于target,则起始数要增大。
极小值情况–排序后的nums的起始四个数为最小,最小还大于target说明不用再找了
2)针对第二个元素同理

def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums= sorted(nums)
        n= len(nums)
        res=[]
        if not nums:
            return res
        for i in range(n-3):
            if i>0 and nums[i]==nums[i-1]: #去除重复项
                continue
            if nums[i]+nums[n-1]+nums[n-2]+nums[n-3]<target:
                continue
            if nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target:
                break
            for j in range(i+1,n-2):
                if j-i>1 and nums[j]==nums[j-1]:#注意j-i>1,去除重复项
                    continue
                if  nums[i]+nums[j]+nums[n-1]+ nums[n-2]<target:
                    continue
                if nums[i]+nums[j]+nums[j+1]+nums[j+2]>target:
                    break
                l = j+1
                r= n-1
                while l<r:
                    if nums[i]+nums[j]+nums[l]+nums[r]==target:
                        res.append([nums[i],nums[j],nums[l],nums[r]])
                        while l<r and nums[l]==nums[l+1]:
                            l+=1
                        while l<r and nums[r]==nums[r-1]:
                            r-=1
                        l+=1
                        r-=1
                    elif nums[i]+nums[j]+nums[l]+nums[r]>target:
                        r-=1
                    else :
                        l+=1
        return res
7.最接近的三数之和(medium)

题目:给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

思路:1)类似求解三数之和,再算多一步三数之和与target的差值,将和,差值存入哈希表,取哈希表差值最小对应的和。
2)也可以将差值比较后直接保存最小值,不用哈希表

 def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums= sorted(nums)
        length = len(nums)
        res={}
        for n in range(length-2):
            l= n+1
            r= length-1
            while l<r:
                s = nums[n]+nums[l]+nums[r]
                ans = abs(s-target)
                res[ans]=s
                if s>target:
                    r-=1
                elif s<target:
                    l+=1
                else:
                    return s
        return res[min(res.keys())]
        #第二种----------------------------------------------
        nums= sorted(nums)
        length = len(nums)
        ans = nums[0]+nums[1]+nums[2]
        for n in range(length-2):
            l= n+1
            r= length-1
            while l<r:
                s = nums[n]+nums[l]+nums[r]
                if abs(s-target) <abs(ans-target):
                    ans= s
                if s>target:
                    r-=1
                elif s<target:
                    l+=1
                else:
                    return s
        return ans

8.最短无序连续子数组(easy)

题目:给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

思路:
1)初始化左边界left=len(nums),right=0,
从前往后,依次找到当前数值大于下一个数的情况,eg:[1,3,2],当前数 i 为3时,下一个数 j 为2,此时左边界应变成3的位置(当前的数与左边界对比取最小值即最左边不满足升序条件的),右边界应为2的位置(下一个数与右边界对比取最大值即最尾部不满足升序条件的)
注意:之前想当然以为从前往后,找到当前的数大于下一个数的作为左边界,从后往前,找到当前数小于前一个数的作为右边界。但这样是不行的,未考虑特殊情况,例如[1,3,2,2]当后面是有序,前面无序时,按照这种思路就默认了右边界,但此时不正确的数是比后面有序的数字还大需要再排到后面的。

 def findUnsortedSubarray(self, nums: List[int]) -> int:
        n= len(nums)
        left=n
        right=0
        for i in range (n-1): 
            for j in range(i+1,n):
                if nums[i]>nums[j]:
                    right=max(j,right) 
                    left =min(i,left)
        if right-left>0:
            return right-left+1
        else:
            return 0

2)先对数组排序,再跟原数组对比,记录出现不一样的数字的索引,用从中这些索引中最大值减去最小值+1即为所求区间长度

def findUnsortedSubarray(self, nums: List[int]) -> int:
#使用了zip函数【将迭代器里对应的元素打包成一个个元组,返回打包为元组的列表】
#eg. a = [1,2] b = [4,5] z=zip(a,b)  -->[(1, 4), (2, 5)]
        diff=[]
        for i,(a,b) in enumerate(zip(nums, sorted(nums))):
            if a!= b:
                diff.append(i)
        return len(diff) and max(diff)-min(diff)+1
 #---------------不用zip---------------------------
        diff=[]
        nums_sort = sorted(nums)
        for i in range(len(nums)):
            if nums[i] != nums_sort[i]:
                diff.append(i)
        return len(diff) and max(diff)-min(diff)+1
9.至少是其他数字两倍的最大数(easy)

题目:在一个给定的数组nums中,总是存在一个最大元素 。查找数组中的最大元素是否至少是数组中每个其他数字的两倍。如果是,则返回最大元素的索引,否则返回-1。

思路:只需要判断最大元素是否是第二大元素的2倍或者2倍以上即可,需先定义最大值和次大的值,以及最大值的索引

def dominantIndex(self, nums: List[int]) -> int:
        m_index= 0
        maxs=0
        maxsec=0
        for i in range(len(nums)):
            if nums[i]>maxs:
                maxsec=maxs
                maxs=nums[i]
                m_index=i
            elif nums[i]>maxsec:
                maxsec=nums[i]
        if maxs>=maxsec*2:
            return m_index
        else:
            return -1
10.合并区间(medium)

题目:给出一个区间的集合,请合并所有重叠的区间。

思路:先按照区间的第一个元素排序,依次遍历区间,合并的条件就是当前区间 i 的右边界 i[1] 要大于下一个区间的左边界(i+1)[0],合并后的右边界根据当前和下一个区间的右边界哪个大来决定,即i[1]与i+1[1]取最大值,如果不满足条件则保留区间

def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x: x[0])  # 先按区间左边界值由小到大排序
        res =[]
        i =0
        while i < len(intervals):
            l = intervals[i][0] #当前区间的左边界
            r = intervals[i][1] #当前区间的右边界
            while (i <len(intervals)-1 )and (intervals[i+1][0]<=r):  #当前区间的右边界大于下一区间的左边界
                    i+=1
                    r = max(intervals[i][1],r) #取最大值为右边界
            res.append([l,r])
            i+=1
        return res
11.螺旋矩阵(medium)

题目: 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

思路:初始化左右上下四个边界为l=0,r=len(metric[0])-1,t=0,b=len(metric)-1。result[]保存结果。当l<=r and t<=b,进入循环:【左上角为(t,l).右下角(d,r)】
从左到右:y从l到r,x=t,循环完t+=1(以防重复),并判断t>b,是的话退出循环
从上到下:x从t到b,y=r,循环完r-=1,并判断l>r
从右到左:y从r到l,x=b,循环完b-=1并判断t>b
从下到上,x从b到t,y = l,循环完 l+=1,并判断l>r

def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix :
           return []
        t=0
        b=len(matrix)-1
        l=0
        r = len(matrix[0]) - 1
        result=[]
        while t<=b and l<=r:
            for y in range(l,r+1):
                x=t
                result.append(matrix[x][y])
            t+=1
            if t >b:break
            for x in range(t,b+1):
                y=r
                result.append(matrix[x][y])
            r-=1
            if r<l:break
            for y in range(r,l-1,-1):
                x=b
                result.append(matrix[x][y])
            b-=1
            if b<t:break
            for x in range(b,t-1,-1):
                y = l
                result.append(matrix[x][y])
            l+=1
            if l>r:break
        return result
12.螺旋矩阵 II (medium)

题目: 给定一个正整数 n,生成一个包含 1 到 n**2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

思路:在11题的基础上,将每次从矩阵中对应的位置取值改为对应位置添加值

 def generateMatrix(self, n: int) -> List[List[int]]:
        l=0
        r=n-1
        t=0
        b=n-1
        res = [[0 for _ in range(n)] for _ in range(n)]
        num=1
        while num<=n*n:
            for y in range(l,r+1):
                x=t
                res[x][y]=num
                num+=1
            t+=1
            for x in range(t,b+1):
                y=r
                res[x][y]=num
                num+=1
            r-=1
            for y in range(r,l-1,-1):
                x=b
                res[x][y]=num
                num+=1
            b-=1
            for x in range(b,t-1,-1):
                y=l
                res[x][y]=num
                num+=1
            l+=1
        return res
13.盛最多水的容器(medium)

题目:给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。说明:你不能倾斜容器,且 n 的值至少为 2
在这里插入图片描述

思路:其实就是木桶效应,盛最多水的容器体积的宽取决于短边,长则为两条直线的距离。
于是问题转化为遍历数组,采用双指针,left指针指向第一条,right指向最后一条,
每求两条直线中的短边,计算此时的体积并保存。
当left为短边,向前遍历,长边保持不变;
当right为短边时,向后遍历,直到left和right相遇则遍历完数组。
计算最大体积时,可在保存体积时,顺便比较当前体积与之前保留的最大体积,每次保留最大的体积,最后保存的就是最大的体积了,比较笨的方法就是新建一个数组,每次都存进数组,最后再取数组中的最大值

 def maxArea(self, height: List[int]) -> int:
        res=0
        l=0
        r=len(height)-1
        while l<r:
            if height[l]>height[r]:
                col=height[r]
                r-=1
            else:
                col=height[l]
                l+=1
            row=r-l+1
            res =max(res,row*col)
        return res
14.接雨水(hard)

题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述

思路:有凹陷的地方才能存水,即高度为先减后增的趋势。因此数组包含的元素至少要3个以上。
思路:用栈来存储柱子下降的趋势即前一个柱子,存的是其索引值。
从左到右遍历:当stack 存在并且当前柱子大于stack顶元素时,弹出栈顶元素top,如果此时栈为空,则跳出循环。
存水的面积的高度h为{min(当前柱子的高度,栈顶元素在其柱子中的高度)【此时的栈顶元素是刚弹出后的下一个了】-刚弹出的栈顶元素}【木桶原理】
宽度则为当前idex-stack[-1]-1;栈存的是索引!!如果栈不存在则往里面添加index。

 def trap(self, height: List[int]) -> int:
        if len(height)<3:
            return 0
        res=0
        stack=[]
        i=0
        while i <len(height):
            while len(stack)>0 and height[i]>height[stack[-1]]:
                top=stack.pop()
                if len(stack)==0:
                    break
                h=min(height[i],height[stack[-1]])-height[top]
                w = i-stack[-1]-1
                res+=(h*w)
            stack.append(i)
            i+=1
        return res

15.搜索旋转排序数组(medium)

题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。

思路:果然这种就是属于一看就会,一写就废的题【边界问题】你的算法时间复杂度必须是 O(logn) 级别—》进一步暗示我们要用二分查找。

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        length = len(nums)
        if length==0:
            return -1
        l=0
        r=length-1
        while l<=r:
            mid = l +(r-l)//2
            if nums[mid]==target:
                return mid
            if nums[mid] >= nums[l]:
                if nums[l] <=target<nums[mid]:
                    r = mid-1
                else:
                    l = mid+1
            else:
                if nums[mid]<target<=nums[r]:
                    l = mid+1
                else:
                    r = mid-1
        return  -1

16.数组中数字出现的次数(medium)

题目:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)

思路:1).将数组进行排序,然后当前和下一个数字一致的话,就将当前数字跳一格。不同的话则将当前数字存入列表中,并采用n标记存入的数的个数,当已经找到这两个只出现一次的数字时就可以提前结束遍历了。
2)也可以采用Counter函数,统计每个数字出现次数,取为一的加入列表中

#1)--------------------------
def singleNumbers(self, nums: List[int]) -> List[int]:
        nums =sorted(nums)
        res = []
        n = 0
        i=0
        while i < len(nums) -1:
            if nums[i]!=nums[i+1]:
                res.append(nums[i])
                n+=1
                i+=1
            else:
                i+=2
            if n<2 and i == len(nums)-1:
                res.append(nums[i])
                n+=1
            if n == 2:
                return res
#2)--------------------------    
def singleNumbers(self, nums: List[int]) -> List[int]:
        ans = Counter(nums)
        res = []
        for val, c in ans.items():
            if c == 1:
                res.append(val)  
        return res
17.最大子序和(easy)

题目:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

思路:设置两个变量分别表示当当前最大连续子序列和,最大子序和。
当当前最大连续子序列和大于0 时,则说明后序的数值可以直接加入,
反之,当前最大连续子序列和小于0 ,说明是负数则直接以当前层来更新当前最大连续子序列和
每次都更新最大子序和

 def maxSubArray(self, nums: List[int]) -> int:
        max_num =nums[0]				#保存连续数组的最大和
        Sum=0							#初始化当前最大连续子序列和为0
 
        if len(nums)==1:
            return nums[0]
        for i in nums:
            if Sum>0:					#当前最大连续子序列和大于0
                Sum+= i					#则直接加上当前层的数值
            else:
                Sum=i					#因为加上前面的数反而越来越小了,所以以当前层开始重新计算最大连续子序列和
            max_num =max(Sum,max_num)	#更新最大子序和
        return max_num
18.和可被 K 整除的子数组(medium)

题目:给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。
示例:
输入:A = [4,5,0,-2,-3,1], K = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 K = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

思路:通常,涉及连续子数组问题的时候,考虑使用前缀和来解决。
采用哈希表+排列组合的思路:用哈希表记录前缀和的数值,以及其出现的次数,由同余定理(两个数除以同个数k得到的余数相同,说明存在被k整除的数。eg,a=3,b=8,k=5,(b-a)==0)可知,出现次数大于1的次数排列组合就是最终结果。

def subarraysDivByK(self, A: List[int], K: int) -> int:
        h = dict()
        h[0]=1
        res=0
        Sum=0
        for i in A:
            Sum +=i				#前缀和
            rem = Sum%K			#求余
            if rem not in h:	
                h[rem]=1
            else:
                h[rem]+=1
        for i,x in h.items():
            res += x*(x-1)//2 	#根据出现的次数算排列组合
        return res
        
19.合并两个有序数组(easy)

题目:给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

思路:
1.双指针,从后往前遍历:
以p2遍历nums2为基准,将num2合并到nums1中。
设置p1指向nums1的m-1位置,p2 指向nums2尾部即n-1位置,p3为在nums1基础上改动合并nums2形成有序数组,从后往前遍历【数字呈递减】,当p1>p2时,则p3指向的值等于p1指向的值。反之则等于p2,当p1遍历完nums1,则直接将nums2剩下的数添加到nums1
最后返回num1

     Do not return anything, modify nums1 in-place instead.
        """
        p1 = m-1
        p2 = n-1
        p3 = m+n-1
        while p2>=0:
            if p1>=0 and nums1[p1]>nums2[p2]:
                nums1[p3] = nums1[p1]
                p1-=1
            else:
      		 #包括两种情况,一种是p1<0时,一种是p1>=0,并且 nums1[p1]<nums2[p2]
                nums1[p3] = nums2[p2] 
                p2-=1
            p3-=1
        return nums1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值