leetcode数组中的问题(八)

 

目录

1013. 将数组分成和相等的三个部分

697. 数组的度

896. 单调数列

1232. 缀点成线

面试题53 - II. 0~n-1中缺失的数字

1018. 可被 5 整除的二进制前缀

54. 螺旋矩阵

1346. 检查整数及其两倍数是否存在

面试题04. 二维数组中的查找

扫描二维码关注公众号,回复: 10927231 查看本文章

189. 旋转数组


1013. 将数组分成和相等的三个部分

https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum/

给定一个整数数组 A,只有我们可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。形式上,如果我们可以找出索引 i+1 < j 且满足 (A[0] + A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + A[A.length - 1]) 就可以将数组三等分。

示例 1:输出:[0,2,1,-6,6,-7,9,1,2,0,1],输出:true。解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
示例 2:输入:[0,2,1,-6,6,7,9,-1,2,0,1]。输出:false
示例 3:输入:[3,3,6,5,-2,2,5,1,-9,4],输出:true,解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4
提示:3 <= A.length <= 50000,-10000 <= A[i] <= 10000

思路

一:从前往后找,找出三个和均是总和1/3的区间。

class Solution(object):
    def canThreePartsEqualSum(self, A):
        """
        :type A: List[int]
        :rtype: bool
        """
        total = sum(A)
        if total % 3 != 0:
            return False
        j, i, flag = 0, 0, False
        for i in range(3):
            target, flag = 0, False
            while j < len(A):
                target += A[j]
                j += 1
                if target == total // 3:
                    flag = True
                    break

        if i == 2 and j == len(A) and flag:
            return True
        if i == 2 and flag:
            target = total // 3
            while j < len(A):
                target += A[j]
                j += 1
            if target != total // 3:
                return False
            return True
        return False
                
                

二:由两侧往中间寻找,若左侧右侧都有和为总和的1/3的话,且中间区域还有元素,则符合要求。

class Solution(object):
    def canThreePartsEqualSum(self, A):
        if len(A) <= 2:
            return False
        total = sum(A)
        if total % 3 != 0:
            return False
        
        target = total // 3
        l, r, l_ans, r_ans = 1, len(A) - 2, A[0], A[-1]

        while l < r:
            if l_ans != target:
                l_ans += A[l]
                l += 1
            if r_ans != target:
                r_ans += A[r]
                r -= 1
            # r + 1 > l确保中间还有元素
            if l_ans == target and r_ans == target and r + 1 > l:
                return True
        return False

三:网上看的题解,虽然也提交通过了,但是存在特例的问题,例如[0,0,0,0]应该是True,但是程序返回False。

class Solution(object):
    def canThreePartsEqualSum(self, A):
        total = sum(A)
        if total % 3 != 0:
            return False
        
        target, cnt = 0, 0

        for j in range(len(A)):
            target += A[j]
            if target * 3 == total:
                cnt += 1
                target = 0
        
        return cnt == 3

697. 数组的度

https://leetcode-cn.com/problems/degree-of-an-array/

给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

示例 1:输入: [1, 2, 2, 3, 1],输出: 2,解释: 输入数组的度是2,因为元素1和2的出现频数最大,均为2.连续子数组里面拥有相同度的有如下所示:[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2],最短连续子数组[2, 2]的长度为2,所以返回2.
示例 2:输入: [1,2,2,3,1,4,2],输出: 6
注意:nums.length 在1到50,000区间范围内。nums[i] 是一个在0到49,999范围内的整数。

思路

一:滑动窗口,用字典rec记录每个元素出现的次数,并求出数组的度max_freq。再通过滑动窗口,每次保证右指针均向前移动,当右指针的元素在窗口中出现了max_freq次,则该窗口符合要求,我们寻找以右指针结尾的最短的符合要求的窗口,即向前移动左指针。

from collections import defaultdict
class Solution(object):
    def findShortestSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        rec, max_freq = defaultdict(int), 0
        for num in nums:
            rec[num] += 1
            max_freq = max(max_freq, rec[num])
        if max_freq == 1:
            return 1
        
        l, rec_cur = 0, defaultdict(int)
        res = len(nums)
        for r in range(len(nums)):
            rec_cur[nums[r]] += 1
            if rec_cur[nums[r]] == max_freq:
                while nums[l] != nums[r]:
                    rec_cur[nums[l]] -= 1
                    l += 1
                res = min(res, r - l + 1)
        return res

        

二:借鉴leetcode的题解https://leetcode-cn.com/problems/degree-of-an-array/solution/shu-zu-de-du-by-leetcode/,若num的元素出现频次是max_freq,即以第一个num起始至最后一个num结束,就是与 nums 拥有相同大小的度的连续子数组。用字典rec来记录一些信息,其中键为元素,值为一个列表,列表的第一个元素,是num第一次出现的位置,第二个元素是至今num最后出现的位置,第三个元素是num至今出现的次数。得到rec之后,我们只要列表第三个元素是max_freq的,则该列表的第二个元素减去第一个元素是其一个可行解,所有可行解中取最小。

class Solution(object):
    def findShortestSubArray(self, nums):
        rec, max_freq = {}, 0
        for i, num in enumerate(nums):
            if num not in rec:
                rec[num] = [i, i, 0]
            rec[num][1], rec[num][2] = i, rec[num][2] + 1
            max_freq = max(max_freq, rec[num][2])
        if max_freq == 1:
            return 1

        res = len(nums)

        for k, pair in rec.items():
            if pair[2] == max_freq:
                res = min(res, pair[1] - pair[0] + 1)     
        return res

896. 单调数列

https://leetcode-cn.com/problems/monotonic-array/

如果数组是单调递增或单调递减的,那么它是单调的。如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递减的。当给定的数组 A 是单调数组时返回 true,否则返回 false。

示例 1:输入:[1,2,2,3],输出:true
示例 2:输入:[6,5,4,4],输出:true
示例 3:输入:[1,3,2],输出:false

思路

一:两次遍历,分别看是否非递减、是否非递增。

class Solution(object):
    def isMonotonic(self, A):
        """
        :type A: List[int]
        :rtype: bool
        """
        if len(A) <= 1:
            return True
        
        flag_P, flag_N = True, True

        for i in range(1, len(A)):
            if A[i - 1] > A[i]:
                flag_P = False
                break
        for i in range(1, len(A)):
            if A[i-1] < A[i]:
                flag_N = False
                break
        return flag_P or flag_N

二:借鉴https://leetcode-cn.com/problems/monotonic-array/solution/dan-diao-shu-lie-by-leetcode/官方题解,若数组单调则所有相邻两值的差都必须同号,cmp函数:要在一次遍历中执行该检查,我们将会处理由 {−1,0,1}组成的比较流,分别对应  <==,或 >。

class Solution(object):
    def isMonotonic(self, A):
        if len(A) <= 1:
            return True
        
        store = 0

        for i in range(1, len(A)):
            c = self._cmp(A[i-1], A[i])
            if c:
                if c != store and store != 0:
                    return False
                store = c

        return True

    def _cmp(self, a, b):
        if a < b:
            return -1
        elif a == b:
            return 0
        else:
            return 1
        

1232. 缀点成线

https://leetcode-cn.com/problems/check-if-it-is-a-straight-line/

在一个 XY 坐标系中有一些点,我们用数组 coordinates 来分别记录它们的坐标,其中 coordinates[i] = [x, y] 表示横坐标为 x、纵坐标为 y 的点。请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回 true,否则请返回 false。

示例 1:

输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
输出:true

思路

一:直接由数学公式来的,所有的点与第一个点的斜率相同,则是一条直线,因为涉及到除法,此处将0单独讨论了。

class Solution(object):
    def checkStraightLine(self, coordinates):
        """
        :type coordinates: List[List[int]]
        :rtype: bool
        """
        if len(coordinates) <= 2:
            return True
        n = len(coordinates)
        dx, dy = [0] * (n - 1), [0] * (n - 1)

        for i in range(1, n):
            dx[i-1] = coordinates[i][0] - coordinates[0][0]
            dy[i-1] = coordinates[i][1] - coordinates[0][1]
        v, h = True, True
        for i in range(0, len(dx)):
            if dx[i] != 0:
                v = False
                break
        for i in range(0, len(dy)):
            if dy[i] != 0:
                h = False
                break
        if v or h:
            return True

        for i in range(len(dx)):
            if dx[i] == 0 or dy[i] == 0:
                return False
        
        theta = 1.0 * dy[0] / dx[0]
        for i in range(len(dx)):
            if 1.0 * dy[i] / dx[i] != theta:
                return False
        return True
        
        
        
        

二:为了避免单独处理0,可以将除法操作改为乘法操作。

class Solution(object):
    def checkStraightLine(self, coordinates):
        if len(coordinates) <= 2:
            return True
        n = len(coordinates)
        dx, dy = [0] * (n - 1), [0] * (n - 1)

        for i in range(1, n):
            dx[i-1] = coordinates[i][0] - coordinates[0][0]
            dy[i-1] = coordinates[i][1] - coordinates[0][1]
        for i in range(1, len(dx)):
            if dx[i] * dy[0] != dx[0] * dy[i]:
                return False
        return True

面试题53 - II. 0~n-1中缺失的数字

https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:输入: [0,1,3],输出: 2
示例 2:输入: [0,1,2,3,4,5,6,7,9],输出: 8
限制:1 <= 数组长度 <= 10000

思路

一:因为有序,可用二分法,第一个不等于下标的元素的下标即为缺失值,此处r=len(nums),而不是len(nums)-1,是因为若只有一个元素,后者不会进入循环,则返回的是nums[0],这种是错误的。只要进去循环了,l必定是第一个不等于下标的元素的下标。

class Solution(object):
    def missingNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) == 1:
            if nums[0] == 0:
                return 1
            return 0
        l, r = 0, len(nums)


        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] <= mid:
                l = mid + 1
            else:
                r = mid 
        return l

1018. 可被 5 整除的二进制前缀

https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/

给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。

示例 1:输入:[0,1,1],输出:[true,false,false],解释:输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:输入:[1,1,1],输出:[false,false,false]
示例 3:输入:[0,1,1,1,1,1],输出:[true,false,false,false,true,false]
示例 4:输入:[1,1,1,0,1],输出:[false,false,false,false,false]
提示:1 <= A.length <= 30000,A[i] 为 0 或 1

思路

一:直接移位,但是后面的数据会越来越大,python中貌似没有溢出,但是运行时间高达300ms+。

二:参照lee题code上的大神题解,https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/solution/gen-ju-qu-mo-yun-suan-gui-ze-ji-suan-by-clementiv/,根据余数定理(a+b)%q=(a%q+b%q)%q,(a * b) % p = (a % p * b % p) % p

可以每添加一位求一次余数,然后在余数的基础上继续求下一位,依次类推。

class Solution(object):
    def prefixesDivBy5(self, A):
        """
        :type A: List[int]
        :rtype: List[bool]
        """
        res, mat = [False] * len(A), 0

        for i in range(0, len(A)):
            mat = mat * 2 + A[i]
            if mat % 5 == 0:
                res[i] = True
            # 对5取余不改变最终结果,还会提高运行效率。
            mat %= 5
        return res

54. 螺旋矩阵

https://leetcode-cn.com/problems/spiral-matrix/

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

示例 1:

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

输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

思路

一:转载leetcode官方题解的法一,https://leetcode-cn.com/problems/spiral-matrix/solution/luo-xuan-ju-zhen-by-leetcode/,模拟。

直觉

绘制螺旋轨迹路径,我们发现当路径超出界限或者进入之前访问过的单元格时,会顺时针旋转方向。

算法

假设数组有 m行 n 列,visited[x][y]表示第 x 行第 y 列的单元格之前已经被访问过了。当前所在位置为 (x,y),前进方向是 di。我们希望访问所有 m*n个单元格。当我们遍历整个矩阵,下一步候选移动位置是 (tx ,ty)。如果这个候选位置在矩阵范围内并且没有被访问过,那么它将会变成下一步移动的位置;否则,我们将前进方向顺时针旋转之后再计算下一步的移动位置。

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix or not matrix[0]:
            return []
        m, n = len(matrix), len(matrix[0])
        visited = [[False] * n for _ in range(m)]

        dx = [0, 1, 0, -1]
        dy = [1, 0, -1, 0]

        res, x, y, di = [], 0, 0, 0

        for i in range(m * n):
            res.append(matrix[x][y])
            visited[x][y] = True
            tx = x + dx[di]
            ty = y + dy[di]
            if not(0 <= tx < m and 0 <= ty <n) or visited[tx][ty]:
                di += 1
                di %= 4
            x += dx[di]
            y += dy[di]
        return res

二:转载leetcode官方题解的法二,https://leetcode-cn.com/problems/spiral-matrix/solution/luo-xuan-ju-zhen-by-leetcode/,按层模拟

直觉

答案是最外层所有元素按照顺时针顺序输出,其次是次外层,以此类推。

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix or not matrix[0]:
            return []
        m, n = len(matrix), len(matrix[0])
        x1, x2, y1, y2 = 0, m - 1, 0, n - 1
        res = []
        while x1 <= x2 and y1 <= y2:
            for j in range(y1, y2 + 1):
                res.append(matrix[x1][j])
            for i in range(x1 + 1, x2 + 1):
                res.append(matrix[i][y2])
            if x1 < x2 and y1 < y2:
                for j in range(y2 - 1, y1 - 1, -1):
                    res.append(matrix[x2][j])
                for i in range(x2 - 1, x1, -1):
                    res.append(matrix[i][y1])
            x1 += 1
            x2 -= 1
            y1 += 1
            y2 -= 1
                
        return res

1346. 检查整数及其两倍数是否存在

https://leetcode-cn.com/problems/check-if-n-and-its-double-exist/

给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即,N = 2 * M)。更正式地,检查是否存在两个下标 i 和 j 满足:i != j,0 <= i, j < arr.length,arr[i] == 2 * arr[j]
示例 1:输入:arr = [10,2,5,3],输出:true,解释:N = 10 是 M = 5 的两倍,即 10 = 2 * 5 。
示例 2:输入:arr = [7,1,14,11],输出:true,解释:N = 14 是 M = 7 的两倍,即 14 = 2 * 7 。
示例 3:输入:arr = [3,1,7,11],输出:false,解释:在该情况下不存在 N 和 M 满足 N = 2 * M 。
提示:2 <= arr.length <= 500,-10^3 <= arr[i] <= 10^3

思路

一:暴力解法,直接两重循环遍历。

二:排序+双指针,此方法参考leetcode官方题解,https://leetcode-cn.com/problems/check-if-n-and-its-double-exist/solution/jian-cha-zheng-shu-ji-qi-liang-bei-shu-shi-fou-cun/,此处将数组分为大于等于零和小于零排序,因为这两种情况指针移动方法不一样,例如x>=0时,r指针只需要一直前进,若在前进过程中找到一个比2x大的数字,则2x必然不存在,在l前进的过程中,l所指向的x会不断增大,2x 也会不断递增,因此指针 r 不需要后退。

class Solution(object):
    def checkIfExist(self, arr):
        """
        :type arr: List[int]
        :rtype: bool
        """
        a1 = [item for item in arr if item >= 0]
        a1 = sorted(a1)

        a2 = [item for item in arr if item < 0]
        a2 = sorted(a2, reverse=True)

        l, r = 0, 0
        for l in range(len(a1)):
            while r < len(a1) and a1[r] < 2 * a1[l]:
                r += 1
            if r < len(a1) and r != l and a1[l] * 2 == a1[r]:
                return True

        l, r = 0, 0
        for l in range(len(a2)):
            while r < len(a2) and a2[r] > 2 * a2[l]:
                r += 1
            if r < len(a2) and r != l and a2[l] * 2 == a2[r]:
                return True
        return False

面试题04. 二维数组中的查找

https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/

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

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。给定 target = 20,返回 false。

限制:0 <= n <= 1000,0 <= m <= 1000

思路

一:两重循环遍历每一个元素,时间复杂度O(m*n)

二:遍历每一层,对该层的元素用二分查找,时间复杂度O(m*lg(n)),在此基础上稍微优化了下,二分查找的右指针无须每次都从n开始,该右指针必定在上一层右指针的左边,直接用上一层右指针即可,无须重新赋值。

class Solution(object):
    def findNumberIn2DArray(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix or not matrix[0]:
            return False
        m, n = len(matrix), len(matrix[0])
        r = n
        for i in range(m):
            l = 0
            while l < r:
                mid = l + (r - l) // 2
                if matrix[i][mid] == target:
                    return True
                if matrix[i][mid] < target:
                    l = mid + 1
                else:
                    r = mid
        return False

三:copyleetcode的https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/jian-er-zhi-zhi-er-fen-cha-zhao-by-liweiwei1419-3/大神题解,时间复杂度O(m+n)。

class Solution(object):
    def findNumberIn2DArray(self, matrix, target):
        if not matrix or not matrix[0]:
            return False
        m, n = len(matrix), len(matrix[0])
        r, c = 0, n - 1
        while r < m and c >=0:
            if matrix[r][c] == target:
                return True
            if matrix[r][c] < target:
                r += 1
            else:
                c -= 1
        return False

189. 旋转数组

https://leetcode-cn.com/problems/rotate-array/

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

示例 1:输入: [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:输入: [-1,-100,3,99] 和 k = 2,输出: [3,99,-1,-100]
解释: 向右旋转 1 步: [99,-1,-100,3],向右旋转 2 步: [3,99,-1,-100]
说明:尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。要求使用空间复杂度为 O(1) 的 原地 算法。

思路

一:暴力:旋转k次,每次讲数组旋转一个元素,时间复杂度O(k*n),超时

class Solution(object):
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        if len(nums) <= 1:
            return nums
        n = len(nums)
        k = k % n 
        for i in range(k):
            last = nums[-1]
            for j in range(n - 2, -1, -1):
                nums[j + 1] = nums[j]
            nums[0] = last

二:使用额外的数组,将每个元素直接放到最终的位置,即i放在(i+k)%n的位置上,时间复杂度O(n)。

class Solution(object):
    def rotate(self, nums, k):
        if len(nums) <= 1:
            return nums
        n = len(nums)
        helper = nums[:]
        for i in range(n):
            nums[(i + k) % n] = helper[i]
        

三:借鉴官方题解方法三,https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/,使用环状替换。

class Solution(object):
    def rotate(self, nums, k):
        if len(nums) <= 1:
            return nums
        n, i, pre, count = len(nums), 0, 0, 0
        for pre in range(len(nums)):
            if count >= len(nums):
                return 
            idx, i = (pre + k) % n, pre 
            replace = nums[i]
            while idx != pre:
                idx = (i + k) % n 
                old = nums[idx]
                nums[idx] = replace
                replace = old
                i = idx
                count += 1

四:三次反转,转自https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/官方题解,

class Solution(object):
    def rotate(self, nums, k):
        if len(nums) <= 1:
            return nums
        n = len(nums)
        k = k % n 

        self._reverse(nums, 0, n - 1)
        self._reverse(nums, k, n - 1)
        self._reverse(nums, 0, k - 1)

    def _reverse(self, nums, l, r):
        while l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
            r -= 1
发布了46 篇原创文章 · 获赞 1 · 访问量 5048

猜你喜欢

转载自blog.csdn.net/qq_xuanshuang/article/details/104658862