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)
發佈了20 篇原創文章 · 獲贊 7 · 訪問量 2873
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章