Leetcode刷题 (1)

 

 

题目

只出现一次的数字

搜索二维矩阵 II

求众数

合并两个有序数组


 

只出现一次的数字

 

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

说明:

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

示例 1:

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

示例 2:

输入: [4,1,2,1,2]
输出: 4

看到这道题目的时候,第一个naive的想法是:

用一个额外的list的index值来表示这个数的值,如果出现了就讲这个index的位置+1,然后最后遍历这个额外的数组,找到值为1的位置。

然鹅不行呀,首先这个额外数组得设置成多大呢?题目没有限制这个数组中的int有多大。其次,题目有一个很关键的信息

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

于是我又想到了另外一种方法:

给这个数组排序,注意到“除了某个元素只出现一次以外,其余每个元素均出现两次”,那么排序后,数组的偶数位如果和奇数位不相等,那就是要的答案

这样做是可以AC的,但是其实还是不满足题目的要求:虽然没有用额外的空间,但是一个排序怎么说最少也是 O(nlogn) 的时间复杂度,严格来说还是不可以。

于是我的小脑瓜已经不能想到一种线性的算法了,这时候看看别人的算法也是可以

巧妙借助异或的方法。因为异或这种运算有两种性质

  • 0和任何数异或都是这个数本身
  • 一个数和自身异或为0
  • (a ^ b) ^ c = a ^ (b ^ c)

所以呢,如果给数组中的所有数都异或,那么最终结果只剩下那个形单影只的数啦~

代码如下:

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums.sort()
        for i in range(0, len(nums)-1, 2):
            if nums[i] != nums[i+1]:
                return nums[i]
        return (nums[len(nums)-1])

其实在知道算法之后,在coding上还出现了两个问题:

  1. nums.sort()sorted(nums) ,前者是针对数组对象的方法,后者是针对数组的函数。sort和sorted要区别~
  2. 题目要输出的是这个形单影只的数,而不是这个数的位置,读题还是不仔细~

搜索二维矩阵 II

 

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例:

现有矩阵 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


这个题目深深诠释了我是如何自己作死的,想不出算法不要紧,陷入死循环的思路才是最可怕的!

我的思路如下

“错误”的根源:我将它看作成一个递归问题

简单的解释一下所以我认为,每次都取最中间的那一行那一列对应的数,与目标数做比较。如果当前数 < 目标数:那么只需要看两个绿色框里的内容,红色部分是不可能有的。

这种思路不是错误的,但使问题变得复杂了的。代码如下:

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if len(matrix) == 0 or len(matrix[0])  == 0:
            return False
        
        idx_0 = {
            'left': 0,
            'right': len(matrix[0]) - 1,
            'low': len(matrix)-1,
            'high': 0
        }
        return self.search(matrix, target, idx_0)

    def search(self, matrix, target, idx):
        """
        :type matrix: List[List[int]]
        :type target: int
        :type idx: dic
        """
        mid_x = (idx['low'] + idx['high']) / 2
        mid_y = (idx['left'] + idx['right']) / 2
        #print idx
        #print mid_x, mid_y

        if (idx['left'] > idx['right'] or idx['high'] > idx['low']):
            return False

        if (idx['left'] == idx['right'] and
                idx['low'] == idx['high'] and
                matrix[mid_x][mid_y] != target):
            return False


        if (matrix[mid_x][mid_y] == target):
            return True
        if (matrix[mid_x][mid_y] > target):
            return (self.search(matrix, target, {
                'left': idx['left'],
                'right': idx['right'],
                'low': mid_x-1,
                'high': idx['high']}) |
                self.search(matrix, target, {
                    'left': idx['left'],
                    'right': mid_y-1,
                    'low': idx['low'],
                    'high': mid_x}))
        else:
                return (self.search(matrix, target, {
                    'left': mid_y + 1,
                    'right': idx['right'],
                    'low': mid_x,
                    'high': idx['high']}) |
                    self.search(matrix, target, {
                        'left': idx['left'],
                        'right': idx['right'],
                        'low': idx['low'],
                        'high': mid_x + 1}))

这个代码是调试了很久才出来的,毕竟递归的算法很容易在编写的时候出现漏洞。

同时在这个过程中也纠正了我对python字典使用方法对误区。可能是之前JS编多了吧,以为python字典的使用是这样的

创建:dic = {a: 1, b: 2}  ❌  调用:dic.a ❌

创建:dic = {'a': 1, 'b': 2}  ✅  调用:dic['a'] ✅

然后好不容易AC了,换来的确是:我的提交执行用时,已经战胜 12.42 % 的 python 提交记录???!!!(黑人问号❓)

实际上比较好想法是这样的

对于这个矩阵,先从左下角开始比较(第0列最下面),如果小于目标数,那第0列可以直接排除;然后看第1列最后一行的数,如果大于目标数,那么最后一行可以直接排除;从第1列倒数第二行的数开始比较, ....

看完为这个想法点?,看来自己的姿势水平还是不够!需要反思下自己的思维模式~

贴上更优方法的代码:

Python (100ms+)

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if len(matrix) == 0 or len(matrix[0]) == 0:
            return False
        
        m = len(matrix)
        n = len(matrix[0])
        
        i = m - 1
        j = 0
        while ( i >= 0 and j < n):
            if matrix[i][j] == target:
                return True
            
            if matrix[i][j] < target:
                j += 1
            else:
                i -= 1
                
        return False

Java (13ms) ???? 手动?

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int row = matrix.length - 1, col = 0;
        while (row < matrix.length && row >= 0 && col < matrix[0].length && col >= 0) {
            if (matrix[row][col] == target) return true;
            if (matrix[row][col] > target) row--;
            else col++;
        }
        return false;
    }
}

(?java代码转载自Leetcode此题的评论内容)


求众数

给定一个大小为 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在众数。

示例 1:

输入: [3,2,3]
输出: 3

示例 2:

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

题目比较重点的信息是: 众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

我的naive思路

排序 --> 从开始找,在n/2大小的窗口中,一定有首尾相同的,即答案

代码如下

class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums.sort()
        n = len(nums)

        for i in range(n/2+1):
            if nums[i] == nums[i+n/2]:
                return nums[i]

然后看了大神的解答后,发现是真的很naive的做法,问题有二:

1)算法题,尽量还是不要用内置的排序函数,这个我以后会注意

2)就算用了排序,实际上只要求排序后数组最中间的数就是答案了

更好的思路:

用一个cnt来代表积分,刚开始cnt = 1,并且ans = 数组中的第0位置的数。往后扫,

如果:积分没有了(cnt = 0),则cnt 重置为1,且ans变为当前位置的数,继续往后扫

如果:当前位置的数等于 ans:积分+1;当前位置的数不等于 ans:积分-1

其实这个思路理解比较抽象,可以具体化成这样一个例子:有很多个队伍互殴,两个人对战两个人都会死,一共有20个人,那么最坏的情况也就是我方的人有11个人,我们派出9人(敌人一共就这么多)去消灭他们,我们还剩下1个人呢✌️。

最终的代码如下:

class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        
        
        cnt = 1
        ans = nums[0]
        for i in range(1, len(nums)):
            if cnt == 0:
                cnt = 1
                ans = nums[i]
                continue
            
            if ans == nums[i]:
                cnt += 1
            else:
                cnt -= 1
        return ans

合并两个有序数组

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 使得 num1 成为一个有序数组。

说明:

  • 初始化 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. 两数组拼接 --> 排序

2. 开一个额外的数组,长度(m+n),然后通过比较两个数组的数字,从小到大把这个额外的数组填满

第一个解法的复杂性是 O( (n+m)log(n+m) ),而且题目已经说了两个数组是排好顺序的,所以这种算法绝对是一个费时费力不讨好的做法。

第二个解法的计算复杂性是 O(m+n),但是额外开了一个数组,题目说的是直接在nums1里面排序(Do not return anything, modify nums1 in-place instead),所以其实这个方法似乎也不是最好的,但是我没想到那个最好的,只能先实现这个笨方法:

class Solution(object):
    def merge(self, nums1, m, nums2, n):
        """
        :type nums1: List[int]
        :type m: int
        :type nums2: List[int]
        :type n: int
        :rtype: None Do not return anything, modify nums1 in-place instead.
        """
        # 先处理一下特殊的情况,两个数列里面有一个是空的
        if m == 0:
            for i in range(n):
                nums1[i] = nums2[i]
            return
        if n == 0:
            return
        
        # 创建一个额外的数组
        ans = [0 for i in range(m+n)]

        
        idx1 = 0 # nums1的index
        idx2 = 0 # nums2的index
        i = 0 # 额外数组的index
        while i < m + n  and idx2 < n and idx1 < m:
            # 如果nums1当前位置的数小于nums2当前位置的数,把此值填到额外数组中,并做相应的位置移动
            while i < m + n and nums1[idx1] <= nums2[idx2]:
                ans[i] = nums1[idx1]
                idx1 += 1
                i += 1
                # nums1已经填完了
                if idx1 >= m:
                    while idx2 < n:
                        ans[i] = nums2[idx2]
                        i += 1
                        idx2 += 1
                    for i in range(m + n):
                        nums1[i] = ans[i]
                    return 

            while i < m + n and nums2[idx2] <= nums1[idx1]:
                ans[i] = nums2[idx2]
                idx2 += 1
                i += 1

                if idx2 >= n:
                    while idx1 < m:
                        ans[i] = nums1[idx1]
                        i += 1
                        idx1 += 1

                    for i in range(m + n):
                        nums1[i] = ans[i]
                    return 

总体编下来的感觉是——不舒服,相当不舒服,循环真的难写!当一个优雅的程序员果然不是一件容易的事情sigh

submit之后还是通过啦,不过理所当然的只战胜了30%左右的人,看来还是不够优化。

看了评论区,才发现如何去实现 O(1) 的空间复杂度!!!!!其实很简单:

把nums1倒转一下,然后从首部开始填数字

如果coding的话和我的其实差不多,只不过这种呢没有利用额外的空间。

总结了一下,大概我没有想到这种方法的原因是:惯性思维让我潜意识以为需要把 [1, 3, 5] 和 [2,4] 这两个数组合并排序,而不是[1, 3, 5, 0, 0] 和 [2,4]合并排序,所以后边的空位我也想不到原来可以利用呀;其次就是倒转数组,这个思想我也是一时没想到~

 

第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。 
class Solution(object):
    def firstBadVersion(self, n):
        left = 1
        right = n
        
        while left <= right:
            mid = (left + right) / 2
            if isBadVersion(mid):# 防止无限循环
                if right == mid:
                    return right
                else:
                    right = mid
            else:
                left = mid + 1
        return left

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值