、【leetcode】分治、回溯练习题目 50、78、169 、17、51

50 Pow(x, n) (Facebook 在半年内面试常考)

思路

Base Case: n == 0
Function: F(x ^ n) = F(x ^ (n // 2)) * F(x ^ (n // 2))

不论我们返回时候如何,我们执行第一步,先设立Base Case:
if n == 0: return 1

完了以后,我们要对大问题进行拆分,也就是不断的对b的值折半

拆分:
half = self.myPow(x, n // 2)

当拆分到了最小的问题,满足base case n == 0 的时候,我们则进行返回,返回时候有三种可能

Function的三种可能性:

  • 当n为偶数的时候,比如 2 ^ 100,拆分的时候就变成 (2 ^ 50) * (2 ^ 50)
  • 当n为基数的时候,比如 2 ^ 25,拆分的时候就变成 (2 ^12) * (2 ^ 12) * 2
  • 当n为负数的时候,返回 1.0 / self.myPow(x, -n)

、【leetcode】分治、回溯练习题目 50、78、169 、17、51_第1张图片
参考

复杂度

时间复杂度 = 一叉树里面每层的时间复杂度 * 层数 = 1 * log(b) = log(b)
空间复杂度 = O(h) 也就是一叉树的层数 = log(b)

 Solution:
    def myPow(self, x, n):
        if n == 0:
            return 1
        if n < 0:
            return 1.0 / self.myPow(x, -n)
        half = self.myPow(x, n // 2)
        if n % 2 == 0:
            return half * half 
        return half * half * x

78 子集(Facebook、字节跳动、亚马逊在半年内面试中考过)

# 方法1:调用库函数
from itertools import combinations
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        return [s for n in range(len(nums) + 1) for s in combinations(nums,n)]

思路

因为本题求解的是不重复的子集得问题,所以可以每次把新的元素放在之前已经拿出来的元素的位置,并且保留之前拿出来的结果,
、【leetcode】分治、回溯练习题目 50、78、169 、17、51_第2张图片

# 简化的写法
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = [[]]
        for i in nums:
            res += [[i] + num for num in res]
        return res 

169 多数元素 (亚马逊、字节跳动、Facebook 在半年内面试中考过)

方法一:字典的方法

时间复杂度是 O(M*N)的

from collections import Counter
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        counter = Counter(nums)
        return [k for k, v in counter.items() if v > len(nums) // 2][0]

方法二:摩尔投票法

时间复杂度是:O(n)

参考
动画演示

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        major = 0
        count = 0 
        for num in nums:
            if count == 0:
                major = num 
            if num == major:
                count += 1
            else:
                count -= 1
        return major

方法三:分治法

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        if not nums:return 0
        if len(nums) == 1:return nums[0]
        mid = len(nums) // 2
        a = self.majorityElement(nums[:mid])
        b = self.majorityElement(nums[mid:])
        if a == b:
            return a
        return [a,b][nums.count(b) > mid]

、【leetcode】分治、回溯练习题目 50、78、169 、17、51_第3张图片
True 是 1,False 是 0

17 电话号码的字母组合(亚马逊在半年内面试常考)
采用分治的思想

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        mapping = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno',
                    '7':'pqrs','8':'tuv','9':'wxyz'}
        if not digits:return []
        if len(digits) == 1:
            return list(mapping[digits[0]])
        pre = self.letterCombinations(digits[:-1])
        additional = mapping[digits[-1]]
        return [s + c for s in pre for c in additional]

51 N 皇后(字节跳动、苹果、谷歌在半年内面试中考过)
这个题需要用到一点平面几何的知识,来进行左右对角线路径的判断,如下图所示:

  • 主对角线有 行号 + 列号 = 常数
  • 次对角线有 行号 - 列号 = 常数
    、【leetcode】分治、回溯练习题目 50、78、169 、17、51_第4张图片
def solveNQueens(self, n):
    # queue 存着每一行的皇后的位置,即他的每一行的元素的位置在哪里
    # xy_dif 的意思是 x-y ,代表一条对角线
    # xy_sum 的意思是x + y,代表另一条对角线
    def DFS(queens, xy_dif, xy_sum):
        p = len(queens)
        # 下面是递归终止条件 
        if p==n:  # 说明已经将每一列的皇后已经加满了,
            result.append(queens)
            return None
        #  遍历当前一行所在的所有列
        for q in range(n):  
            # 判断列是否在之前的列里面,是不是在和之前的这些左对角线和右对角线相冲突,
            # 如果没有冲突的话,就把它加到下一行
            if q not in queens and p-q not in xy_dif and p+q not in xy_sum: 
                DFS(queens+[q], xy_dif+[p-q], xy_sum+[p+q])  
    result = []
    DFS([],[],[])
    # 为了按照题目的要求输出一个二维数组
    return [ ["."*i + "Q" + "."*(n-i-1) for i in sol] for sol in result]

你可能感兴趣的:(算法和数据结构刷题,笔记,leetcode,面试,数据结构)