leetcode(136,169,240,88,125,167,215,347)

1.只出现一次的数字(136,简单)

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

说明:

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

示例1:
输入:[2,2,1]
输出:1
示例2:
输入:[4,1,2,1,2]
输出:4
解法一:

我首先想到的比较蠢的方法,直接存到字典里,把数组里的数当作key,如果字典里没有这个键值就设为1,有的话就加一,最后取出值为1的key值。

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dict = {}
        for item in nums:
            if item in dict:
                dict[item] += 1
            else:
                dict[item] = 1
        for key in dict:
            if dict[key] == 1:
                return key
解法二:

首先要了解一个事情,异或操作,两个相同的数异或结果为0,一个数跟0异或是这个数本身,异或是对于二进制来说的,一个数异或自己本身结果为零(全部二进制位相同) 一个数异或零结果为这个数本身(0和0异或为0,1和0异或为1,所以结果还是原来的数) 而一个数异或另一个数两次就是相当于一个数异或零 所以结果还是这个数本身。所以当数组中只有一个数出现了一次,其他数都出现两次时,初始化一个0对数组里每一个数进行异或,结果就是这个数本身。

python的异或符号为^

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        num = 0
        for i in nums:
            num ^= i
        return num

2.求众数(169,简单)

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

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

示例1:
输入: [3,2,3]
输出: 3
示例2:
输入: [2,2,1,1,1,2,2]
输出: 2
解法一:

和上一题思路一样,定义一个字典,key值为数组的数字,value为它出现的次数,最后从字典里找出value值大于n/2的值。

class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dict = {}
        for i in nums:
            if i in dict:
                dict[i] += 1
            else:
                dict[i] = 1
        num = len(nums)/2
        for i in dict:
            if dict[i]>num:
                return i 

改进:
可以用max函数,关于max函数可以参考这篇文章

dict = {}
for i in nums:
    if i in dict:
        dict[i] += 1
    else:
        dict[i] = 1
return max(dict.items(), key=lambda x: x[1])[0]

dict.items()返回的是可以遍历的(键,值)元组数组

print(dict.items()) 
# dict_items([(1, 3), (2, 4)])
print(max(dict.items(), key=lambda x: x[1]))
# (2,6)
print(max(dict.items(), key=lambda x: x[1])[0])
# 2
解法二:

比较鸡贼的方法,由于众数的数量是大于n/2的,所以我们把nums用sorted函数排序,得到的list的第n/2个就是所求答案。

class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        return sorted(nums)[len(nums)/2]

3.搜索二维矩阵Ⅱ(240,中等)

编写一个高效的算法来搜索 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
        for i in range(len(matrix)):
            for j in range(len(matrix[i])):
                if matrix[i][j] == target:
                    return True
        return False

写的更简洁的方法:

class Solution:
    def searchMatrix(self, matrix, target):
        
        if not matrix:
            return  False
        
        for x in matrix:
            for c in x:
                if target== c:
                    return True
        return False
解法二:

对于每一行用二分法:

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        for i in range(len(matrix)):
            first = 0
            last = len(matrix[i]) - 1
            while first <= last:
                mid = (last + first) // 2
                if target < matrix[i][mid]:
                    last = mid - 1
                elif target > matrix[i][mid]:
                    first = mid +1
                else:
                    return True
        return False

这种方法没有利用到每一列也是排序好的信息,不是最佳。

解法三:

我们可以观察到,对于矩阵的右上角,所有在它下面的数都比他大,所有在他左边的数都比他小,所以我们可以从右上角的数开始与target对比,target比它大行数就加一,比它小列数就减一。

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix:
            return False
        row = 0
        col = len(matrix[0]) - 1
        rows = len(matrix)-1
        while row <= rows and col >= 0:
            if target == matrix[row][col]:
                return True
            elif target > matrix[row][col]:
                row += 1
            else:
                col -= 1
        return False

4.合并两个有序数组(88,简单)

给定两个有序整数数组 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]
解法一:
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.
        """
        while m > 0 and n > 0:
            if nums1[m-1] > nums2[n-1]:
                nums1[m+n-1] = nums1[m-1]
                m -= 1
            else:
                nums1[m+n-1] = nums2[n-1]
                n -= 1
        if n > 0:
            nums1[:n] = nums2[:n]

        return None

5.验证回文串(125,简单)

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

回文串就是正读反读相同。

说明: 本题中,我们将空字符串定义为有效的回文串。

示例1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例2:
输入: "race a car"
输出: false

这道题主要是对字符串的操作不太熟悉,思路很清晰,把字符串转换成只包含字母和数字的列表,之后比较原列表和倒序后的列表是否相同就可以了。

解法一:
class Solution(object):
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        list = []
        for i in s:
            if i.isalpha() or i.isdigit():
                list.append(i.lower())
        return True if list == list[::-1] else False

后来发现,其实没必要转成列表,字符串也可以实现同样的操作,原理一样。

解法二:
class Solution(object):
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        s = s.lower()
        newStr = ""
        for i in s:
            if i.isalnum():
                newStr += i
        return newStr==newStr[::-1]

最后实测,用第一种列表的方法速度要快了十倍左右。

解法三:

最简洁的解法,用到了一个高级的函数filter。

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

filter(function, iterable)
# funcion --判断函数
# iterable --可迭代对象
# 注意: Pyhton2.7 返回列表,Python3.x 返回迭代器对象

python2解法:

class Solution(object):
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: boolp
        """
        s = filter(str.isalnum, str(s.lower()))
        return s == s[::-1]

python3解法:

class Solution:
    def isPalindrome(self, s: str) -> bool:
        s = list(filter(str.isalnum, s.lower()))
        return s == s[::-1]

可以看到python3的filter返回对象必须list之后才能继续操作,否则会报错:

Line 4: TypeError: 'filter' object is not subscriptable

6.两数之和Ⅱ-输入有序数组(167,简单)

给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1必须小于index2。

说明:
  • 返回的下标值(index1 和 index2)不是从零开始的。
  • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
解法:

这题主要还是抓住题目中数组是升序排列的条件,可以设置两个指针,分别从数组的最左和最右开始判断,假如比目标值小,左边的指针就加一,比目标值大右边的指针就减一。

class Solution(object):
    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        left = 0
        right = len(numbers)-1
        while left<right:
            if numbers[left]+numbers[right] == target:
                return [left+1, right+1]
            elif numbers[left]+numbers[right] < target:
                left+=1
            else:
                right-=1

7.数组中的第K个最大元素(215,中等)

在未排序的数组中找到第k个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

解法一:

第k大的元素,只要把数组排序取第K个或第-k个元素就可以了。

list.sort(cmp=None, key=None, reverse=False)
cmp -- 可选参数, 如果指定了该参数会使用该参数的方法进行排序。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。
class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        nums.sort()
        return nums[-k]
解法二:

用了快排的思想,快排的思想就是找一个基准点,把比基准点大的放到左边,比基准点小的放到右边,这样基准点的位置就是正确的,即左边都比他大,右边都比他小,在这道题中,不需要把所有的数组全部排好,假如排好基准点恰好在第K个位置上,那么他就是第k大的元素。

快排的实现思想值得思考。首先设置一个index,然后循环list,比较list当前值与基准值(这里取得最后一个元素)的大小,当比基准值大时,就把list[index]与list[i]互换,之后index加一,最后再把list[index]与list[high]互换,就完成了一次操作,之后再进行判断,来更改high或low的位置继续进行这个操作。

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        low, high = 0, len(nums)-1
        while low <= high:
            pivot = self.findpivot(nums, high, low)
            if pivot == k-1:
                return nums[pivot]
            elif pivot > k-1:
                high = pivot-1
            else:
                low = pivot+1
            
    def findpivot(self, nums, high, low):
        index = low
        pivot = nums[high]
        for i in range(low, high):
            if nums[i] >= pivot:
                nums[index], nums[i] = nums[i], nums[index]
                index += 1
        nums[index], nums[high] = nums[high], nums[index]
        return index
                
解法三:

维护一个最小堆,堆可以看作一个完全二叉树,最小堆的堆顶一定是最小的那个数,最大堆堆顶一定是最大的。

python中内置的堆模块为heapq

heaqp模块提供了堆队列算法的实现,也称为优先级队列算法。

要创建堆,请使用初始化为[]的列表,或者可以通过函数heapify()将填充列表转换为堆。

提供以下功能:

Usage:

heap = []            # creates an empty heap
heappush(heap, item) # pushes a new item on the heap
item = heappop(heap) # pops the smallest item from the heap
item = heap[0]       # smallest item on the heap without popping it
heapify(x)           # transforms list into a heap, in-place, in linear time
item = heapreplace(heap, item) # pops and returns smallest item, and adds
                               # new item; the heap size is unchanged

用堆来实现就比较简单了,循环列表加入堆,当堆的size大于k了就弹出堆顶,最后的堆顶就是第k大的值。

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        import heapq
        heap = []
        heapq.heapify(heap)
        for num in nums:
            heapq.heappush(heap, num)
            if len(heap)>k:
                heapq.heappop(heap)
        return heap[0]
                

8.前K个高频元素(347,中等)

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例2:
输入: nums = [1], k = 1
输出: [1]
说明:
  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
解法一:

用一个字典来统计频次,最后根据字典的value值进行排序,然后循环k次放到列表里返回。

根据字典的value或者key排序要可以用lamada表达式,关于lamada表达式:

Python提供了很多函数式编程的特性,如:map、reduce、filter、sorted等这些函数都支持函数作为参数,lambda函数就可以应用在函数式编程中。如下:
# 需求:将列表中的元素按照绝对值大小进行升序排列
list1 = [3,5,-4,-1,0,-2,-6]
sorted(list1, key=lambda x: abs(x))
当然,也可以如下:

list1 = [3,5,-4,-1,0,-2,-6]
def get_abs(x):
    return abs(x)
sorted(list1,key=get_abs)
只不过这种方式的代码看起来不够Pythonic
class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        dict = {}
        re = []
        for num in nums:
            if num in dict:
                dict[num] += 1
            else:
                dict[num] = 1
        s = sorted(dict.items(), key=lambda x:x[1], reverse=True)
        print(s)
        for i in range(k):
            re.append(s[i][0])
        return re

这种解法的时间复杂度是:

O(nlogn)

不符合题目要求(还是不太懂怎么计算复杂度)

解法二:

求出出现频率最高的k个数,当我们通过dict统计出频率之后,其实和上一题一样都是一个排序之后输出前k大个数,所以依然可以使用堆来实现。

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        dict = {}
        list = []
        re = []
        for num in nums:
            if num in dict:
                dict[num] += 1
            else:
                dict[num] = 1
        heapq.heapify(list)
        for key, value in dict.items():
            if len(list) < k:
                heapq.heappush(list, (value, key))
            elif value > list[0][0]:
                heapq.heapreplace(list, (value, key))
        while list:
            re.append(heapq.heappop(list)[1])
        return re
复杂度分析:
  • 时间复杂度:O(nlogk),其中 n 表示数组的长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n) 的;接着,遍历用于存储元素频率的 map,如果元素的频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,这一系列操作的时间复杂度是 O(nlogk) 的;最后,弹出堆中的元素所需的时间复杂度是 O(klogk) 的。因此,总的时间复杂度是 O(nlogk) 的。
  • 空间复杂度:O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k 个元素,因此,空间复杂度是 O(n) 的。
解法三:

最后,为了进一步优化时间复杂度,可以采用桶排序(bucket sort),即用空间复杂度换取时间复杂度。

第一步和解法二相同,也是统计出数组中元素的频次。接着,将数组中的元素按照出现频次进行分组,即出现频次为 i 的元素存放在第 i 个桶。最后,从桶中逆序取出前 k 个元素。

桶排序详见这里

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        dict = {}
        re = []
        for num in nums:
            if num in dict:
                dict[num] += 1
            else:
                dict[num] = 1
        bucket = [[] for _ in range(len(nums) + 1)]
        # 这里的输出是  [[], [], [], [], [], [], [], [], [], []]
        for key, value in dict.items():
            bucket[value].append(key)
        for i in range(len(nums), -1, -1): # range(start,stop,step) 不包含stop,所以到-1,就是到0
            if bucket[i]:
                re.extend(bucket[i])
                if len(re) >= k:
                    break
        return re[:k]

extend和append的区别就是,append会把整体当作一个元素加到列表里,extend会把整体拆成元素加进去,如:

music_media = ['compact disc', '8-track tape', 'long playing record']
new_media = ['DVD Audio disc', 'Super Audio CD']
music_media.append(new_media)
print music_media
>>>['compact disc', '8-track tape', 'long playing record', ['DVD Audio disc', 'Super Audio CD']]
复杂度分析
  • 时间复杂度:O(n),其中n表示数组的长度。
  • 空间复杂度:O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值