leetcode数组中的问题(六)

目录

1266. 访问所有点的最小时间

258. 各位相加

1252. 奇数值单元格的数目

面试题 01.01. 判定字符是否唯一

1051. Height Checker

1064. 不动点

561. 数组拆分 I

1217. 玩筹码

922. 按奇偶排序数组 II

1150. 检查一个数是否在数组中占绝大多数


1266. 访问所有点的最小时间

https://leetcode-cn.com/problems/minimum-time-visiting-all-points/

平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi]。请你计算访问所有这些点需要的最小时间(以秒为单位)。

你可以按照下面的规则在平面上移动:每一秒沿水平或者竖直方向移动一个单位长度,或者跨过对角线(可以看作在一秒内向水平和竖直方向各移动一个单位长度)。必须按照数组中出现的顺序来访问这些点。

示例 1:输入:points = [[1,1],[3,4],[-1,0]],输出:7,解释:一条最佳的访问路径是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0] ,从 [1,1] 到 [3,4] 需要 3 秒 ,从 [3,4] 到 [-1,0] 需要 4 秒,一共需要 7 秒
示例 2:输入:points = [[3,2],[-2,2]],输出:5
提示:points.length == n,1 <= n <= 100,points[i].length == 2,-1000 <= points[i][0], points[i][1] <= 1000

思路

一:横纵距离之差dx,dy(其中dx和dy均大于等于0),此时:

  1. dx > dy:先沿对角线走dy步,再沿x方向走dx - dy步,即一共需dx步;
  2. dx = dy:沿对角线走dy步,即一共需dy步;
  3. dx < dy:先沿对角线走dx步,再沿y方向走dy - dx步,即一共需dy步;

从上述可见需max(dx,dy)步

class Solution(object):
    def minTimeToVisitAllPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        if len(points) <= 1:
            return 0
        res = 0
        for i in range(1, len(points)):
            dx = abs(points[i][0] - points[i - 1][0])
            dy = abs(points[i][1] - points[i - 1][1])
            res += max(dx, dy)
        return res

258. 各位相加

https://leetcode-cn.com/problems/add-digits/

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。

示例:输入: 38,输出: 2 ,解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。
进阶:你可以不使用循环或者递归,且在 O(1) 时间复杂度内解决这个问题吗?

思路

一:借用字符串取各个位置上的数值,并求和。若和为1位数,则返回该和;否则继续循环,将该和继续借用字符串取各个位置上的数值,并求和

class Solution(object):
    def addDigits(self, num):
        """
        :type num: int
        :rtype: int
        """
        if num == 0:
            return 0
        res = sum(int(i) for i in str(num))
        while True:
            if res < 10:
                return res
            res = sum(int(i) for i in str(res))

二:转自leetcode上两位大神的解法,https://leetcode-cn.com/problems/add-digits/solution/java-o1jie-fa-de-ge-ren-li-jie-by-liveforexperienc/https://leetcode-cn.com/problems/add-digits/solution/python-hua-tu-jiang-ming-bai-o1zuo-fa-de-yuan-li-b/,参考第二位大神的思路以及第一位大神的当数值是9的倍数的时候的trick,当然渣渣并没有看懂第一位大神的其余分析。

class Solution(object):
    def addDigits(self, num):
        if num == 0:
            return 0
        
        return (num - 1) % 9 + 1

1252. 奇数值单元格的数目

https://leetcode-cn.com/problems/cells-with-odd-values-in-a-matrix/

给你一个 n 行 m 列的矩阵,最开始的时候,每个单元格中的值都是 0。另有一个索引数组 indices,indices[i] = [ri, ci] 中的 ri 和 ci 分别表示指定的行和列(从 0 开始编号)。你需要将每对 [ri, ci] 指定的行和列上的所有单元格的值加 1。请你在执行完所有 indices 指定的增量操作后,返回矩阵中 「奇数值单元格」 的数目。

示例 1:

输入:n = 2, m = 3, indices = [[0,1],[1,1]],输出:6,解释:最开始的矩阵是 [[0,0,0],[0,0,0]]。第一次增量操作后得到 [[1,2,1],[0,1,0]]。最后的矩阵是 [[1,3,1],[1,3,1]],里面有 6 个奇数。
示例 2:

输入:n = 2, m = 2, indices = [[1,1],[0,0]],输出:0,解释:最后的矩阵是 [[2,2],[2,2]],里面没有奇数。
提示:1 <= n <= 50,1 <= m <= 50,1 <= indices.length <= 100,0 <= indices[i][0] < n,0 <= indices[i][1] < m。

思路

一:时间复杂度O(L + M * N)。这边row[i]表示下标为i的行应该加几次1,col[j]表示下标为j的列应该加几次1,则(i,j)位置加1的次数应该为row[i] + col[j],若该值为奇数,计数res加1。

class Solution(object):
    def oddCells(self, n, m, indices):
        """
        :type n: int
        :type m: int
        :type indices: List[List[int]]
        :rtype: int
        """
        row, col = [0] * n, [0] * m

        for pos in indices:
            row[pos[0]] += 1
            col[pos[1]] += 1
        
        res = 0

        for i in range(n):
            for j in range(m):
                if (row[i] + col[j]) % 2:
                    res += 1
        return res

二:对一进行时间的优化,时间复杂度O(L+M+N),L-indices列表的长度。row_odd表示只考虑行的时候,共有多少个值为奇数行,遍历列的下标,若对应的列是奇数列,则只有偶数行的交叉点才是奇数,即n-row_odd,若对应的列是偶数列,则只有奇数行的交叉点才是奇数,即row_odd。方法一还做了件额外的事,即(i,j)的元素的值,其自然会时间复杂度高一些。

class Solution(object):
    def oddCells(self, n, m, indices):
        # row[i] - i行被加1的次数,co l[j] - j列被加1的次数
        row, col, res = [0] * n, [0] * m, 0
        for pos in indices:
            row[pos[0]] += 1
            col[pos[1]] += 1

        # row_odd -  不考虑列时,行为奇数值的个数   
        row_odd = 0
        for i in range(n):
            if row[i] % 2:
                row_odd += 1
        for j in range(m):
            if col[j] % 2:
                # 奇数(列) + 偶数(行) = 奇数
                # 该列奇数的个数,即对应偶数行的个数
                res += (n - row_odd)
            else:
                # 偶数(列) + 奇数(行) = 奇数
                # 该列奇数的个数,即对应奇数行的个数
                res += row_odd         
        return res

面试题 01.01. 判定字符是否唯一

https://leetcode-cn.com/problems/is-unique-lcci/

实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

示例 1:输入: s = "leetcode",输出: false 
示例 2:输入: s = "abc",输出: true
限制:0 <= len(s) <= 100,如果你不使用额外的数据结构,会很加分。

思路

一:借助集合

二:借助排序

三:转leetcode一位大佬的题解,https://leetcode-cn.com/problems/is-unique-lcci/solution/wei-yun-suan-fang-fa-si-lu-jie-shao-by-zhen-zhu-ha/,用位运算。位运算方法的思路本质上,跟使用一个bool数组来记录astr的每一位是否已经出现过的思路是一样的。

基于bool数组的方法:
由于题目没有明确说明,根据示例我判断字符串中出现的字符应该在['a','z']之间,实践证明确实如此。基于这个前提,使用bool数组的做法是定义一个长度为26的初始值全为0 bool数组,逐个字符遍历astr,如果bool数组中对应的下标('a'->0, ..., 'z'->25)的值为1则重复出现,返回false,否则设置对应下标值为1。

基于位运算的方法:
我们可以使用一个int类型的变量(下文用mark表示)来代替长度为26的bool数组。假设这个变量占26个bit(在多数语言中,这个值一般不止26),那么我们可以把它看成000...00(26个0),这26个bit对应着26个字符,对于一个字符c,检查对应下标的bit值即可判断是否重复。那么难点在于如何检查?这里我们可以通过位运算来完成。首先计算出字符char离'a'这个字符的距离,即我们要位移的距离,用move_bit表示,那么使用左移运算符1 << move_bit则可以得到对应下标为1,其余下标为0的数,如字符char = 'c',则得到的数为000...00100,将这个数跟mark做与运算,由于这个数只有一个位为1,其他位为0,那么与运算的结果中,其他位肯定是0,而对应的下标位是否0则取决于之前这个字符有没有出现过,若出现过则被标记为1,那么与运算的结果就不为0;若之前没有出现过,则对应位的与运算的结果也是0,那么整个结果也为0。对于没有出现过的字符,我们用或运算mark | (1 << move_bit)将对应下标位的值置为1。

class Solution(object):
    def isUnique(self, astr):
        """
        :type astr: str
        :rtype: bool
        """
        if not astr:
            return True
        mark = 0
        for c in astr:
            step = ord(c) - ord("a")
            val = 1 << step
            if mark & val:
                return False
            mark |= val
        return True

1051. Height Checker

https://leetcode-cn.com/problems/height-checker/

Students are asked to stand in non-decreasing order of heights for an annual photo.Return the minimum number of students that must move in order for all students to be standing in non-decreasing order of height.

Example 1:Input: heights = [1,1,4,2,1,3],Output: 3,解释:排序完毕之后应该是[1,1,1,2,3,4],只有4,1,3的位置不在正确的位置上,共3个数字。
Constraints:1 <= heights.length <= 100,1 <= heights[i] <= 100

思路

一:先排序,看看有几个数字和排好序的数字不一致的。

class Solution(object):
    def heightChecker(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        h = sorted(heights)
        res = 0
        for i in range(len(h)):
            if h[i] != heights[i]:
                res += 1
        return res

二:计数排序的思路,但其实效率应该与法一差不了太多,毕竟列表长度不超过100。

class Solution(object):
    def heightChecker(self, heights):
        cnt = [0] * 101
        for num in heights:
            cnt[num] += 1
        count, j = 0, 0
        for i in range(1, len(cnt)):
            while cnt[i] >= 1:
                if i != heights[j]:
                    count += 1
                cnt[i] -= 1
                j += 1    
        return count

1064. 不动点

https://leetcode-cn.com/problems/fixed-point/

给定已经按升序排列、由不同整数组成的数组 A,返回满足 A[i] == i 的最小索引 i。如果不存在这样的 i,返回 -1。

示例 1:输入:[-10,-5,0,3,7],输出:3,解释:对于给定的数组,A[0] = -10,A[1] = -5,A[2] = 0,A[3] = 3,因此输出为 3。
示例 2:输入:[0,2,5,8,17],输出:0
示例:A[0] = 0,因此输出为 0 。
示例 3:输入:[-10,-5,3,4,7,9],输出:-1
解释: ,不存在这样的 i 满足 A[i] = i,因此输出为 -1 。
提示:1 <= A.length < 10^4,-10^9 <= A[i] <= 10^9。

思路

一:遍历,因为升序,故第一次遇到的 A[i] == i下标i,就是最小的。

二:本以为该题不能用二分的,但是注意到题目中有不同整数,再加之有序,故二分也可。若有符合的只可能是l下标,因为A[r]一定大于等于r,A[l-1]是小于l-1的最后一个数,故有可能符合的只是A[l]。例如[-1, 1, 2, 3],r最终指向下标1,即元素1,l指向下标1,即元素1。注意与面试题 08.03. 魔术索引https://leetcode-cn.com/problems/magic-index-lcci/)的区别,该题中没有说数值是不同的,即仅凭有序,我们就算确定了A[mid]与mid的关系,也不能断定可行解的值A[i] == i会出现在哪一部分,例如[0,0,2],mid=1时,其前半部分和后半部分均有可行解。而该题一旦A[mid]<mid,则前半部分一定木有可行解,前半部分都比下标小,若有在后半部分。若有符合的只可能是l下标,因为A[r]一定大于等于r,A[l-1]是小于l-1的最后一个数,故有可能符合的只是A[l]。

class Solution:
    def fixedPoint(self, A):
        if not A or A[0] >= len(A) or A[-1] < 0:
            return -1
        l, r = 0, len(A)
        while l < r:
            mid = l + (r-l) // 2
            if A[mid] >= mid:
                r = mid 
            else:
                l = mid + 1
        if A[l] == l:
            return l
        return -1
        

561. 数组拆分 I

https://leetcode-cn.com/problems/array-partition-i/

给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。

示例 1:输入: [1,4,3,2],输出: 4,解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4)。
提示:n 是正整数,范围在 [1, 10000]。数组中的元素范围在 [-10000, 10000]。

思路

一:先调用库函数排序,然后每隔一个取一个。有一种理解思路,我们想取n个尽量大的数,将列表由大到小排序,先取第一对的数,因为二者取最小,最大的数只能取到第二大的数,且必须和第一大的数一对,排除掉这俩数,再剩下的数里依照该法继续取,即是在有序数组中,每隔一个取数。

class Solution(object):
    def arrayPairSum(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums = sorted(nums)
        res = 0
        for i in range(0, len(nums), 2):
            res += nums[i]
        return res

二:用计数排序,每俩个组队,同一个位置的若刚好能够两两组队,则组队,并将配的对数乘上该值加上(加入其和的一半);若两两配对还少一个,则预支后面的一个数,并将配的对数乘上该值加上;若该数被预支了,则减去一个,余下的两两配对。

class Solution(object):
    def arrayPairSum(self, nums):
        arr = [0] * 20001

        for num in nums:
            arr[num - 10001] += 1
        
        bias, res, b = -10000, 0, 0

        for i in range(len(arr)):
            res += (i + bias) * ((arr[i] + 1 - b) // 2 )
            b = (arr[i] - b) % 2
        return res

1217. 玩筹码

https://leetcode-cn.com/problems/play-with-chips/

数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):将第 i 个筹码向左或者右移动 2 个单位,代价为 0。将第 i 个筹码向左或者右移动 1 个单位,代价为 1。最开始的时候,同一位置上也可能放着两个或者更多的筹码。返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。

示例 1:输入:chips = [1,2,3],输出:1,解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。
示例 2:输入:chips = [2,2,2,3,3],输出:2,解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。
提示:1 <= chips.length <= 100,1 <= chips[i] <= 10^9

思路

一:首先,数组中存的是位置信息,移动两步不用付出代价,移动一步耗费代价1,可将所有的数看看能不能通过移动两步合并到下标0的位置上,若能count_2加一(这类的移到下标0的位置不需要付出代价,归位一类),若不能则count_1加1(这类表示若移到下标0的位置不需要付出1),count_1和count_2取最小即可。

class Solution(object):
    def minCostToMoveChips(self, chips):
        """
        :type chips: List[int]
        :rtype: int
        """
        count_1, count_2 = 0, 0 

        for item in chips:
            if abs(item - chips[0]) % 2:
                count_1 += 1
            else:
                count_2 += 1
        
        return min(count_1, count_2)

二:由思路一演化过来,判断位置的奇偶性即可,奇数间相互移动不要付代价,偶数间相互移动也不要付代价,奇偶移动需付出代价1。

class Solution(object):
    def minCostToMoveChips(self, chips):
        odd, even = 0, 0 

        for item in chips:
            if item % 2:
                odd += 1
            else:
                even += 1
        
        return min(odd, even)

922. 按奇偶排序数组 II

https://leetcode-cn.com/problems/sort-array-by-parity-ii/

给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。你可以返回任何满足上述条件的数组作为答案。

示例:输入:[4,2,5,7],输出:[4,5,2,7],解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。
提示:2 <= A.length <= 20000,A.length % 2 == 0,0 <= A[i] <= 1000

思路

一:奇偶双指针法,A 中一半整数是奇数,一半整数是偶数,所以我们可以保证只要奇数正确,则偶数必然正确,反之亦然,故此处保证奇数位正确,odd指向奇数下标,even指向偶数下标,当奇数下标上是奇数时,奇数下标+2到下一个奇数下标,若奇数下标不是奇数,则开始寻找偶数下标,若偶数下标是偶数位,继续寻找下一个偶数下标,若偶数下标是奇数,则交换,交换完之后两个指针均往前挪。

class Solution(object):
    def sortArrayByParityII(self, A):
        """
        :type A: List[int]
        :rtype: List[int]
        """
        odd, even = 1, 0

        while odd < len(A):
            if A[odd] % 2:
                odd += 2
            else:
                while even < len(A):
                    if A[even] % 2:
                        A[even], A[odd] = A[odd], A[even]
                        even += 2
                        break
                    even += 2
                odd += 2
        return A

1150. 检查一个数是否在数组中占绝大多数

https://leetcode-cn.com/problems/check-if-a-number-is-majority-element-in-a-sorted-array/

给出一个按 非递减 顺序排列的数组 nums,和一个目标数值 target。假如数组 nums 中绝大多数元素的数值都等于 target,则返回 True,否则请返回 False。所谓占绝大多数,是指在长度为 N 的数组中出现必须 超过 N/2 次。

示例 1:输入:nums = [2,4,5,5,5,5,5,6,6], target = 5,输出:true,解释:数字 5 出现了 5 次,而数组的长度为 9。所以,5 在数组中占绝大多数,因为 5 次 > 9/2。
示例 2:输入:nums = [10,100,101,101], target = 101,输出:false,解释:数字 101 出现了 2 次,而数组的长度是 4。所以,101 不是 数组占绝大多数的元素,因为 2 次 = 4/2。
提示:1 <= nums.length <= 1000,1 <= nums[i] <= 10^9,1 <= target <= 10^9

思路

一:直接遍历,O(n)的时间复杂度,因为有序,可提前终止,时间复杂度依旧不变。

二:两次二分查找,_lower和_greater分别查找值为target的最小下标以及最大下标,不存在返回-1,时间复杂度O(lgn)。

class Solution(object):
    def isMajorityElement(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: bool
        """
        # 特判
        if nums[len(nums) // 2] != target:
            return False

        g_idx = self._greater(nums, target)
        l_idx = self._lower(nums, target)
        if g_idx == -1 or l_idx == -1:
            return False
        length = g_idx - l_idx + 1
        if length > (len(nums) // 2):
            return True
        return False

    def _lower(self, nums, target):
        # [l, r]
        l, r = 0, len(nums) - 1
        loc = -1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                loc = mid
                l = mid + 1
            else:
                r = mid - 1
        if loc == -1 or nums[loc] != target:
            return -1
        return loc

    def _greater(self, nums, target):
        # [l, r]
        l, r = 0, len(nums) - 1
        loc = -1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] <= target:
                l = mid + 1
            else:
                loc = mid
                r = mid - 1
        if loc == -1 or nums[loc] != target:
            return -1
        return loc

    # def _lower(self, nums, target):
    #     l, r = 0, len(nums)
    #     while l < r:
    #         mid = l + (r - l) // 2
    #         if nums[mid] < target:
    #             l = mid + 1
    #         else:
    #             r = mid
    #     if l >= len(nums) or nums[l] != target:
    #         return -1
    #     return l
    #
    # def _greater(self, nums, target):
    #     l, r = 0, len(nums)
    #     while l < r:
    #         mid = l + (r - l) // 2
    #         if nums[mid] <= target:
    #             l = mid + 1
    #         else:
    #             r = mid
    #     if r - 1 < 0 or nums[r - 1] != target:
    #         return -1
    #     return r - 1
发布了46 篇原创文章 · 获赞 1 · 访问量 5051

猜你喜欢

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