1. 程式人生 > 實用技巧 >資料結構與演算法習題總結——查詢與排序演算法

資料結構與演算法習題總結——查詢與排序演算法

本篇為Datawhale程式設計實踐專案的學習筆記,會隨著學習程序不斷更新,題目都借鑑自網路或書籍,僅用作個人學習。由於水平實在有限,不免產生謬誤,歡迎讀者多多批評指正。如需要轉載請與博主聯絡,謝謝


查詢演算法

例題:

  1. 搜尋插入位置
    給定一個排序陣列和一個目標值,在陣列中找到目標值,並返回其索引。如果目標值不存在於陣列中,返回它將會被按順序插入的位置。你可以假設陣列中無重複元素。
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        '''
        # 解法一:暴力查詢
        if not nums or target <= nums[0]:return 0
        if target > nums[-1]:return len(nums)
        for i in range(1,len(nums)):
            if target <= nums[i] and target > nums[i-1]:  # 小於等於同時達到了查詢和差值的目的
                return i
        '''
        # 解法二:二分查詢
        left = 0
        right = len(nums)    # 考慮了target可能大於最後一個值的情況
        while left < right:
            mid = (left + right) // 2
            if nums[mid] < target:
                left = mid + 1     # 如果沒有相同值,最終一定得到nums[left-1]<target<nums[left]
            elif nums[mid] > target:
                right = mid
            elif nums[mid] == target:
                return mid     # 查到結果,直接輸出
        return left
  1. 快樂數
    編寫一個演算法來判斷一個數 n 是不是快樂數。
    「快樂數」定義為:對於一個正整數,每一次將該數替換為它每個位置上的數字的平方和,然後重複這個過程直到這個數變為 1,也可能是 無限迴圈 但始終變不到 1。如果 可以變為  1,那麼這個數就是快樂數。
    如果 n 是快樂數就返回 True ;不是,則返回 False 。
class Solution:
    def isHappy(self, n: int) -> bool:
        # 思路很簡單,就是迭代計算看是否陷入迴圈(不是快樂數)還是收斂到1(是快樂數)
        def cal(m:int):
            l = list(str(m))
            return sum([pow(int(i),2) for i in l])
        s = set([int(n)])   # 這裡利用集合(HashSet)儲存已出現過的數很關鍵,可以大大提升查詢效率
        while n != 1:
            n = cal(n)
            if n in s:
                return False
            s.add(n)
        return True
  1. 同構字串
    給定兩個字串 s 和 t,判斷它們是否是同構的。
    如果 s 中的字元可以被替換得到 t ,那麼這兩個字串是同構的。
    所有出現的字元都必須用另一個字元替換,同時保留字元的順序。兩個字元不能對映到同一個字元上,但字元可以對映自己本身。
    示例1:
    輸入: s = "egg", t = "add"
    輸出: true
    示例2:
    輸入: s = "foo", t = "bar"
    輸出: false
class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        # 用雜湊表儲存已經出現過的對應替換關係,有重複元素出現時檢查此時兩個字元間的對映是否與之前儲存的匹配
        if not s and not t: return True
        if len(s) != len(t): return False
        dic = {}
        for i in range(len(s)):
            if t[i] in dic:
                if dic[t[i]] != s[i]:
                    return False
            else:
                if s[i] in dic.values():
                    return False
                dic[t[i]] = s[i]
        return True
  1. 有效的字母異位詞
    給定兩個字串 s 和 t ,編寫一個函式來判斷 t 是否是 s 的字母異位詞。
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        # 先生成一個字典用鍵來儲存字元,值記錄字元出現次數;然後根據字典對照另一個字串的字元出現情況即可
        dic = {}
        if len(s) != len(t): return False
        for i in range(len(s)):
            if s[i] in dic:
                dic[s[i]] += 1
            else:
                dic[s[i]] = 1
        for j in range(len(t)):
            if t[j] not in dic:
                return False
            else:
                dic[t[j]] -= 1
            if dic[t[j]] == 0:
                del dic[t[j]]
        return True if not dic else False
  1. 單詞規律
    給定一種規律 pattern 和一個字串 str ,判斷 str 是否遵循相同的規律。
    這裡的 遵循 指完全匹配,例如, pattern 裡的每個字母和字串 str 中的每個非空單詞之間存在著雙向連線的對應規律。
class Solution:
    def wordPattern(self, pattern: str, str: str) -> bool:
        # 思路很簡單,還是用雜湊表來儲存對應關係
        s = str.split()
        if len(s) != len(pattern):return False
        dic = {}
        for i in range(len(pattern)):
            if pattern[i] not in dic:
                if s[i] in dic.values():
                    return False
                dic[pattern[i]] = s[i]
            else:
                if dic[pattern[i]] != s[i]:
                    return False
        return True
  1. 兩個陣列的交集 I
    給定兩個陣列,編寫一個函式來計算它們的交集。
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        '''
        # 解法一:暴力查詢
        res = []
        for i in nums1:
            if i in nums2 and i not in res:
                res.append(i)
        return res
        '''
        # 解法二:投機取巧使用Python集合的求交集方法(不想這樣的話可以用雜湊表做元素的儲存和查詢也很簡單)
        return set(s1) & set(s2)
  1. 兩個陣列的交集 II
    還是上題的要求,但輸出結果中每個元素出現的次數,應與元素在兩個陣列中出現次數的最小值一致。
class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 還是利用雜湊表記錄一個列表中出現的元素及元素出現的次數,然後另一個列表出現的元素與之比較並記錄結果
        dic = {}
        ans = []
        for i in range(len(nums1)):
            if nums1[i] not in dic:
                dic[nums1[i]] = 1
            else:
                dic[nums1[i]] += 1
        for i in range(len(nums2)):
            if nums2[i] in dic:
                if dic[nums2[i]] == 0:
                    continue
                dic[nums2[i]] -= 1
                ans.append(nums2[i])
        return ans
  1. 根據字元出現頻率排序
    給定一個字串,請將字串裡的字元按照出現的頻率降序排列。
class Solution:
    def frequencySort(self, s: str) -> str:
        # 核心思路是字典儲存元素及出現的次數,根據次數排序,然後按出現次數重新在新列表中打印出來
        N = len(s)
        dic = {}
        for i in range(N):
            if s[i] not in dic:
                dic[s[i]] = 1
            else:
                dic[s[i]] += 1
        l = sorted(zip(dic.values(),dic.keys()))    # 將鍵值對打包成元組,注意值要排在前面,因為要按值排序
        ans = [None]*N
        n = 0
        for j in range(len(l)-1,-1,-1):   # 降序排列列印
            for m in range(l[j][0]):
                ans[n] = l[j][1]
                n += 1
        return ''.join(ans)
  1. 有序陣列中的單一元素
    給定一個只包含整數的有序陣列,每個元素都會出現兩次,唯有一個數只會出現一次,找出這個數。注意您的方案應該在 O(log n)時間複雜度和 O(1)空間複雜度中執行。
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        # 這個題麻煩在時間複雜度要求log n,因此不能遍歷查詢。應該用二分法解決。
        if len(nums) == 1:
            return nums[0]
        left = 0
        right = len(nums)-1
        while left <= right:
            mid = (left + right) // 2
            if mid % 2 == 0 and mid + 1 < len(nums):  # mid是偶數(nums[mid]前面有偶數個元素)
                if nums[mid] == nums[mid+1]:   # mid前面沒有單一元素
                    left = mid + 1
                else:   # mid前面有單一元素
                    right = mid - 1
            elif mid % 2 != 0 and mid + 1 < len(nums):   # mid是奇數(nums[mid]前面有奇數個元素)
                if nums[mid] == nums[mid+1]:  # mid前面有單一元素
                    right = mid - 1
                else:   # mid前面沒有單一元素
                    left = mid + 1
            else:
                return nums[mid]
        return nums[left]

參考資料:

  1. https://github.com/datawhalechina/team-learning-program/blob/master/LeetCodeClassification/1.分治.md Datawhale小組學習資料
  2. https://leetcode-cn.com/ 力扣leetcode