算法总结

1.基础算法

1.1 快速排序

def sort(nums):
    """快速排序封装"""
    def quik_sort(nums, l, r):
        if l >= r:
            return
        # 分为两部分
        # k1 记录左边小于nums[r]的位置, k2记录当前遍历到的位置
        k1 = k2 = l
        for k2 in range(l, r):
            if nums[k2] < nums[r]:
                nums[k2], nums[k1] = nums[k1], nums[k2]
                k1 += 1
        # 把最后一个数放到中间来
        nums[k1], nums[r] = nums[r], nums[k1]
        quik_sort(nums, l, k1 - 1)
        quik_sort(nums, k1, r - 1)
    quik_sort(nums, 0, len(nums) - 1)


if __name__ == '__main__':
    a = [1,5,3,2,5,4]
    sort(a)
    print(a)

这个讲解也不错:https://blog.csdn.net/hansionz/article/details/82821811

2.面试常见算法

2.1 数组

2.1.1 最大连续子数组和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # example1: -1,-2,-3,-4
        # 记录该位置前的最大连续和
        cur_s = 0
        max_s = -(2<<31)
        for i in range(len(nums)):
            cur_s = nums[i]+cur_s
            max_s = max(cur_s, max_s)
            # 如果cur_s小于0,丢弃它
            if cur_s < 0:
                cur_s = 0
        return max_s

2.1.2 1~n整数中1出现的次数(找规律)

class Solution:
    """统计每个位置上出现1的次数然后相加"""
    def countDigitOne(self, n: int) -> int:
        # 1,9,10,11,20,21,31,41,91,100,101,102,110,111,112,119,120

        # 比较十位数为0,1,2,3...的情况
        # 十位数 > 1  --> n//100*10 + 9 + 1
        # 2356, 0010-2319 --> 000-239
        # 2359, 0010-2319 --> 000-239
        # 十位数 == 1  --> n//100*10 + n%10 + 1
        # 2319, 0010-2319 --> 000-239
        # 2316, 0010-2316 --> 000-236
        # 十位数 == 0 --> (n//100 - 1) * 10 + 9 + 1
        # 2300, 0010-2219 --> 000-229
        # 2308, 0010-2219 --> 000-229

        # digit位数, res和
        digit, res = 1, 0
        # high高位数, cur本身, low地位数
        high, cur, low = n // 10, n % 10, 0
        # 高位部位0或者地位不为0
        while high != 0 or cur != 0:
            # 当前为0
            if cur == 0: 
                res += high * digit
            # 当前为1
            elif cur == 1: 
                res += high * digit + low + 1
            # 当前大于1
            else: 
                res += (high + 1) * digit
            # 更新地位数字,地位加上本身就变成了地位
            low += cur * digit
            # 下个当前数字就是高位数字 % 10
            cur = high % 10
            # 下个高位数字就是高位数字 // 10
            high //= 10
            # 记录位数
            digit *= 10
        return res

2.1.3 数字序列中某一位的数字(找规律)

class Solution:
    """1-9 十个数,9个数位, 10-99 一百个数 90*2=180数位 , 100-999 一千个数,900*3=2700个数位"""
    def findNthDigit(self, n: int) -> int:
        # 初始数位,开始位置,当前digit所能容纳的数位
        digit, start, count = 1, 1, 9
        # 如果n大于count,那么说明数太大,在后面
        while n > count: # 1.
            # 减去当前容量
            n -= count
            # 更新初始位置
            start *= 10
            # 更新每个阶段单个数字拥有的数字位数
            digit += 1
            # 更新当前digit的容量
            count = 9 * start * digit
        # 如果count>n了,代表当前count装得下n了,当前位置每个数字的数位位digit,那么就可以定位到哪个数
        # 例如: n=11 --> n=n-9=2 --> start = 10,  2,就是10这个位置,所以(n-1)//digit, index = (n-1)%digit
        num = start + (n - 1) // digit # 2.
        # index = (n-1)%digit
        return int(str(num)[(n - 1) % digit]) # 3.

2.1.4 把数组排成最小的数

class Solution:
    """排序依据 x+y>y+x --> x大于y,然后对数组排序就可以"""
    def minNumber(self, nums: List[int]) -> str:


        # nums = [str(_) for _ in nums]
        # def x_big_than_y(x, y):
        #     """排序规则"""
        #     if x+y > y+x:
        #         print(x+y,'-->',y+x,True)
        #         return True
        #     else:
        #         print(x+y,'-->',y+x,False)
        #         return False

        # def fast_sort(i, j):
        #     if i>=j:
        #         return
        #     # 左右的起始位置, l记录左边小于nums[j]的位置
        #     k1=k2=i
        #     for k2 in range(i, j):
        #         # 当前r比末尾小
        #         if x_big_than_y(nums[j], nums[k2]):
        #             nums[k1], nums[k2] = nums[k2], nums[k1]
        #             k1 += 1
        #     # 将末尾移到中间来
        #     nums[j], nums[k1] = nums[k1], nums[j]
        #     fast_sort(i, k1 - 1)
        #     fast_sort(k1, j - 1)
        # fast_sort(0, len(nums) - 1)
        # print(nums)
        # return "".join(nums)


        def fast_sort(l , r):
            # 退出
            if l >= r: 
                return
            # 初始化
            i, j = l, r
            while i < j:
                # 如果末尾大于初始位置,从末尾开始找,找到一个比第一个位置小的数字
                while strs[j] + strs[l] >= strs[l] + strs[j] and i < j: 
                    # 末尾 - 1
                    j -= 1
                # 如果当前大于初始位置,从初始位置开始找,找到第一个比第一个位置大的数字
                while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: 
                    i += 1
                # 交换
                strs[i], strs[j] = strs[j], strs[i]
            # 把初始位置放到中间
            strs[i], strs[l] = strs[l], strs[i]
            # 递归
            fast_sort(l, i - 1)
            # 递归
            fast_sort(i + 1, r)
        
        strs = [str(num) for num in nums]
        fast_sort(0, len(strs) - 1)
        return ''.join(strs)

2.1.5 扑克牌顺子

# leetcode submit region begin(Prohibit modification and deletion)
class Solution(object):
    """
    1.排序
    2.记录0的个数
    3.追踪间隔gap的个数
    4.比较0和gap的个数
    """
    def isStraight(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """

        # 0,0,11,12,13
        # 10,11,12,0,0
        # 0,0,0,11,12

        if not nums:
            return False
        nums.sort()
        zeros = nums.count(0)
        gaps = 0

        # 已经排好序了,那么第一个非0的数字,也就是最小值的位置就知道了
        small = zeros
        # 后面一个数
        big = small + 1
        # 一步一步检查
        while big < len(nums):
            print(small, big)
            if nums[small] == nums[big]:
                return False
            # 记录间隔
            gaps += nums[big] - nums[small] - 1
            small = big
            big += 1
        # 比较间隔和0的个数
        return gaps <= zeros

2.1.6 股票最大价值

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        
        # 方法一:1.记录最小值 2.每次check
        max_gain = 0
        min_price = 1<<31
        for price in prices:
            min_price = min(price, min_price)
            max_gain = max(max_gain, price - min_price)
        return max_gain

2.2 树

2.2.1 二叉搜索树的第k大节点

class Solution(object):
    """树的遍历"""
    def kthLargest(self, root, k):
        """
        :type root: TreeNode
        :type k: int
        :rtype: int
        """
        self.cnt = 0

        def search(node):
            # 如果都查到头了,还没找到,返回None
            if node is None:
                return None
            # 查找右边节点
            right = search(node.right)
            if right is not None:
                return right
            # 查找自己
            if node is not None:
                self.cnt += 1
                # print("===", node.val)
                # print("===", self.cnt)
                if self.cnt == k:
                    # 如果查到了,返回该值,所以查左右的时候要看一看
                    return node.val
            # 查找左边节点
            left = search(node.left)
            if left is not None:
                return left

        return search(root)

2.2.2 树的深度

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        self.max_d = 0
        def dfs(node, level):
            if node is not None:
                self.max_d = max(self.max_d, level)
                dfs(node.left, level + 1)
                dfs(node.right, level + 1)
    
        dfs(root, 1)
        return self.max_d

2.2.3 平衡二叉树判定

  • 方法一 后续遍历,先判断子树是否为平衡树,如果是,返回树的深度,不是,返回-1,如果子树返回-1,那么直接剪枝输出非平衡树结果
class Solution(object):
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """

        # 方法一: 后续遍历,先判断子树是否为平衡树,如果是,返回树的深度,不是,返回-1,如果子树返回-1,那么直接剪枝输出非平衡树结果
        def recur(root):
            if not root:
                return 0
            # 后序遍历
            left = recur(root.left)
            if left == -1:
                return -1
            right = recur(root.right)
            if right == -1:
                return -1
            # 比较左右子树差,如果是平衡树,返回这棵树的深度,不是则返回-1
            return max(left, right) + 1 if abs(left - right) <= 1 else -1
        # 如果返回的为-1,则为非平衡树
        return recur(root) != -1
  • 方法二 从上到下判定是否为平衡二叉树
class Solution(object):

    # 方法二: 从上到下判定是否为平衡二叉树
    def isBalanced(self, root: TreeNode) -> bool:
        # 空节点,就是平衡二叉树 
        if not root: 
            return True
        # 如果左右的深度差小于等于1 并且左右都是平衡二叉树
        return abs(self.depth(root.left) - self.depth(root.right)) <= 1 and \
               self.isBalanced(root.left) and self.isBalanced(root.right)

    def depth(self, root):
        """递归求树的深度"""
        # 空节点,深度为0
        if not root:
            return 0
        # 节点的深度等于左右深度的最大值+1
        return max(self.depth(root.left), self.depth(root.right)) + 1

2.2.4 二叉搜索树的最近公共祖先

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        # 如果跟节点的值小于p值 并且 跟节点的值小于q的值
        if root.val < p.val and root.val < q.val:
            # 那么要找的点肯定在树的右节点上
            return self.lowestCommonAncestor(root.right, p, q)
        # 如果跟节点大于p的值 也大于 q的值
        if root.val > p.val and root.val > q.val:
            # 那么肯定在跟节点的左子树上面
            return self.lowestCommonAncestor(root.left, p, q)
        # 如果一个大于当前节点,一个小于当前节点,那么就是这个点了,否则肯定要继续递归
        return root

2.2.5 二叉树的最近公共祖先

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        # 这个解析的比较好:https://blog.csdn.net/sgbfblog/article/details/7935537
        # 如果为null,直接返回,如果root==p或者q,则向上回溯。
        if not root or root == p or root == q:
            return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right:
            return root
        return left if left else right

2.3 动态规划

2.3.1 把数字翻译成字符串

  • 题目
    给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
class Solution:
    def translateNum(self, num: int) -> int:
        num = str(num)
        n = len(num)
        if n < 2:
            return 1
        # dp[k] 代表第k个有几种翻译方法
        dp = [0 for _ in range(n+1)]
        dp[0] = dp[1] = 1
        for i in range(2, n+1):
            tmp = int(num[i-2:i])
            # print(tmp)
            # 注意这里有一个tmp>=10,代表他为两位数的时候,才考虑这两个数组可以合并
            if tmp < 26 and tmp >= 10:
                dp[i] = dp[i-2] + dp[i-1]
            else:
                dp[i] = dp[i-1]

        # print(dp)
        return dp[n]

2.3.2 礼物最大价值

class Solution(object):
    def maxValue(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m, n = len(grid), len(grid[0])
        dp = [[0 for _ in range(n)] for __ in range(m)]
        
        # 可以直接用grid,不用再新建一个内存了
        dp[0][0] = grid[0][0]

        for j in range(1, n):
            dp[0][j] = dp[0][j-1] + grid[0][j]

        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + + grid[i][0]

        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] += (max(dp[i-1][j], dp[i][j-1])) + grid[i][j]

        print(dp)
        return dp[m-1][n-1]

2.3.3 丑数

我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

class Solution(object):
    
    def nthUglyNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        
        # 2,3,4=2*2,2*3,3*3,3*4,3*5,
        # dp 用来存第几个丑数, 总共有三种模式生成丑数, 小的丑数*[2,3,5], 所以a, b, c就是记录针对2,3,5遍历到哪个比较小的丑数点了.
        dp, a, b, c = [1] * n, 0, 0, 0
        # 这个i 记录是第几个 丑数了
        for i in range(1, n):
            # 分别乘以对应的 "原子" 丑数
            n2, n3, n5 = dp[a] * 2, dp[b] * 3, dp[c] * 5
            # 取最小的并更新对应的index
            dp[i] = min(n2, n3, n5)
            if dp[i] == n2: 
                a += 1
            if dp[i] == n3: 
                b += 1
            if dp[i] == n5: 
                c += 1
        return dp[-1]

2.3.4 n个骰子的点数

# leetcode submit region begin(Prohibit modification and deletion)
class Solution(object):
    def twoSum(self, n):
        """
        思路: 动态规划,dp[n][j]代表扔n次,和为j, 这样与前面扔n-1次的关系是啥?因为最后一次可能扔1,2,3,4,5,6
        dp[n][j] = dp[n-1][j-1] + dp[n-1][j-2] + dp[n-1][j-3]
        :type n: int
        :rtype: List[float]
        """
        if n == 0:
            return []
        dp = [[0.0 for _ in range(n * 6 + 1)] for __ in range(n + 1)]
        # 扔一次所有的可能出现的次数都是1
        for i in  range(1, 1 * 6 + 1):
            dp[1][i] = 1

        print(dp[n])
        # 扔两次以上的话
        for i in range(2, n+1):
            # 骰子的和可以为i 到 6*i
            for j in range(i, 6*i + 1):
                # 本次取j个,可以在上一次1 + j-1, 2+ j-2, ..., n+ j-n,在摇1,2,3,..次取到
                # 这里第n次可以取1-6的任意一个值,只要 j -k 大于0,代表目标为j, 上一次取了j-k, 这次取k就好了,而这次的k可以从1-6
                for k in range(1, 6+1):
                    if j - k <= 0:
                        break
                    dp[i][j] += dp[i-1][j - k]

        total_sum = float(6 ** n)
        # print(dp[n])
        # print(total_sum)

        print(dp[n])

        res = []
        # 这里因为最小值为n了, 最大为n*6
        for i in range(n, n*6 + 1):
            res.append(dp[n][i] / total_sum)
        return res

2.3.5 剪绳子

# leetcode submit region begin(Prohibit modification and deletion)
class Solution(object):
    def cuttingRope(self, n):
        """
        :type n: int
        :rtype: int
        """
        """
        dp[i] 表示到i位置绳子切割后乘积的最大值
        注意dp[i]表示i长的绳子至少切一次
        注意(i-j)与dp[i-j]的区别   i-j之前的绳子一次没切 dp[i-j]表示切了至少一次  两者也应比较
        dp[i]=max(dp[i],dp[i-j]*j,(i-j)*j)
        """
        if n<2:
            return
        dp = [0 for _ in range(n + 1)]
        dp[1] = 1
        dp[2] = 1
        for i in range(3, n + 1):
            for k in range(1,i):
                # 注意(i-j)与dp[i-j]的区别   i-j之前的绳子一次没切 dp[i-j]表示切了至少一次  两者也应比较
                dp[i] = max(dp[i], dp[i-k]*k, dp[i-k]*dp[k], (i-k)*k)
        # print(dp)
        return dp[n] % (1000000007)

2.4 回溯

2.5 图

2.6 双指针

2.6.1 最长不包含重复字符的子字符串

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 可以不新建一个list
        s_list = list(s)
        l = r = 0
        exist_set = set()
        max_length = 0
        while r < len(s_list):
            cur = s_list[r]
            if cur not in exist_set:
                r += 1
                exist_set.add(cur)
                # 随时记录最大值
                max_length = max(max_length, r - l)
            else:
                # 移动l,直到这里面没有出现过cur
                while cur in exist_set:
                    l_cur = s_list[l]
                    exist_set.remove(l_cur)
                    l += 1
        return max_length

2.6.2 和为S的连续正序列

class Solution(object):
    def findContinuousSequence(self, target):
        """
        还是通过从小到大遍历来找,双指针,从1,2开始,一直往右边加数字,如果相等就加到res里面,如果大于,就从左边减去一个数字,
        再加一个跳出条件,左边最小值应该要小于target的一半.
        :type target: int
        :rtype: List[List[int]]
        """

        small, big = 1, 2
        # 最小值最大位置,这样两个连续值就要大于target了
        max_len = (target + 1) / 2
        # 初始数组的和
        curr = small + big
        res = []
        # 但最小值还没到足底啊位置
        while small < max_len:
            # 当前节点和等于目标值
            if curr == target:
                # 记录的是最大值和最小值,所以这么操作
                res.append(range(small, big + 1))
            # 如果当前值大于目标值 并且最小值没到他的最大值
            while curr > target and small < max_len:
                # 向前看
                curr -= small
                # 更新最小值
                small += 1
                # 返回结果
                if curr == target:
                    res.append(range(small, big + 1))
            # 否则继续更新有指针,直到当前值满足target或者大于target
            big += 1
            curr += big
        return res

2.6.3 翻转单词顺序

class Solution(object):
    def reverseWords(self, s):
        """
        :type s: str
        :rtype: str
        """
        # 双指针,从后往前看,
        s = s.strip() # 删除首尾空格
        # 双指针在末尾位置
        i = j = len(s) - 1
        res = []
        while i >= 0:
            # 找到第一个空格,代表找到第一个字母了
            while i >= 0 and s[i] != ' ':
                i -= 1 # 搜索首个空格
            # 送入第一个word
            res.append(s[i + 1: j + 1]) # 添加单词
            # 跳过空格
            while s[i] == ' ':
                i -= 1 # 跳过单词间空格
            # 更新j
            j = i # j 指向下个单词的尾字符
        return ' '.join(res) # 拼接并返回

2.7 链表

2.7.1 输入两个链表,找出它们的第一个公共节点。

  • 思路1: 存到一个堆里面然后倒着输出
  • 思路2: 求A,B的长度以及长度差,然后一个先跑,然后查看相等项
  • 思路3: A+B,B+A组合起来,然后遍历
class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        # 这个不知道错在哪
        # node_a_cache, node_b_cache = [], []  
        # cur_a, cur_b = headA, headB
        # while cur_a:
        #     node_a_cache.append(cur_a)
        #     cur_a = cur_a.next
        # while cur_b:
        #     node_b_cache.append(cur_b)
        #     cur_b = cur_b.next
        # 
        # cur = None
        # if node_a_cache and node_b_cache:
        #     while node_a_cache[-1].val == node_b_cache[-1].val:
        #         cur = node_a_cache[-1]
        #         node_a_cache.pop()
        #         node_b_cache.pop()
        #         if not node_b_cache:
        #             break
        #         if not node_a_cache:
        #             break
        # return cur


        # 思路,把两个链表叠加在一块,A+B,B+A,这样两个链表就一样长了,然后找到链表相等的那个位置
        node1, node2 = headA, headB

        while node1 != node2:
            node1 = node1.next if node1 else headB
            node2 = node2.next if node2 else headA

        # 假设没有交点会怎么样 --> 会遍历到None,直接返回的就是None了
        return node1

2.8 二分法

2.8.1 面试题53 - I. 在排序数组中查找数字 I

class Solution(object):
    def search(self, nums, target):
        """先找到中心点,然后左右遍历,好像会比较慢,因为左右遍历的时候,可能还需要遍历n次, 所以二分法找左右边界点是最快的"""
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """

        def dfs(k1, k2):
            # print('k1:', k1, 'k2:',k2)
            if k1 > k2:
                return 0
            
            if k1 == k2:
                # print(k1)
                if nums[k1] == target:
                    return 1
                else:
                    return 0
            
            mid = (k1 + k2) // 2
            if nums[mid] == target:
                cnt = 1
                # 左右继续找
                l = mid - 1
                while l >= 0 and nums[l] == target:
                    cnt += 1
                    l -= 1
                    
                r = mid + 1
                while r <= k2 and nums[r] == target:
                    r += 1
                    cnt += 1
                return cnt
            
            return dfs(k1, mid - 1) + dfs(mid + 1, k2)
        
        return dfs(0, len(nums) - 1)

2.9 位运算

2.9.1 数组中数字出现的次数

class Solution(object):
    def singleNumbers(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        xor = 0
        num1, num2 = 0, 0
        # 异或一轮,出现两次的那些数字都肖掉了
        # 异或: 相同为0,不相同为1
        for num in nums:
            xor ^= num
        # 找到一个为1的位
        mask = 1
        while xor & mask == 0:
            mask = mask << 1
        # 再遍历一次数组
        for num in nums:
            # 还是进行异或,然后通过与mask的值,来判定挑选出第一个数字和第二个数字
            # 分成两批,与mask相同和不相同,这样异或完就把两个数字区分和捞出来了
            if num & mask == 0:
                num1 ^= num
            else:
                num2 ^= num
        return [num1, num2]

2.9.2 从出现三次的数组中挑出来只出现一次的数字

class Solution(object):
    """从出现三次的数组中挑出来只出现一次的数字"""
    def singleNumber(self, nums):
        # 记录每个bit的cnt,然后这个cnt如果能被3整除,那这个位就不要加进来
        """
        :type nums: List[int]
        :rtype: int
        """

        res = 0
        for i in range(32):
            cnt = 0  # 记录当前 bit 有多少个1
            bit = 1 << i  # 记录当前要操作的 bit
            for num in nums:
                if num & bit != 0:
                    cnt += 1
            if cnt % 3 != 0:
                # 不等于0说明唯一出现的数字在这个 bit 上是1
                res |= bit

        return res - 2 ** 32 if res > 2 ** 31 - 1 else res

2.9.3 不用加减乘除做加法

class Solution(object):
    def add(self, a, b):
        """
        :type a: int
        :type b: int
        :rtype: int
        """
        x = 0xffffffff
        # 获得其补码(python负数的补码有些奇怪,要得到真正的补码需要与16进制数0xffffffff与)
        a, b = a & x, b & x
        while b != 0:
            a, b = (a ^ b), (a & b) << 1 & x
        return a if a <= 0x7fffffff else ~(a ^ x)

2.10 队列

2.10.1 队列最大值

class MaxQueue(object):
    """队列:先进先出. 栈:先进后出
        解题思路:collections.deque(), dp.popleft()
        就是一个记录最大值,一个记录原始的deque,记录最大值的那个,相当于是做了一个缓存,每遇到一个比较大的,
        就把小的替换掉,把大的记录下来,这样在排出队列的时候,我这个最大值会一直记录在这,直到两个相等了,才排出来。
        新增的时候,来一个小的,这个小的也会存在那里,因为只有来一个比较大的时候,才会把前面的小的都替换掉,所以这么
        一个缓存机制,相当于就把最大值以及最大值的断层时刻就记录下来了,当popleft的时候,就可以用了。
    """
    def __init__(self):
        
        import collections
        self.dp = collections.deque()
        self.max_value_dp = collections.deque()


    def max_value(self):
        """
        :rtype: int
        """
        if self.max_value_dp:
            return self.max_value_dp[0]
        else:
            return -1


    def push_back(self, value):
        """
        :type value: int
        :rtype: None
        """
        self.dp.append(value)
        while self.max_value_dp and self.max_value_dp[-1] < value:
            self.max_value_dp.pop()
        self.max_value_dp.append(value)



    def pop_front(self):
        """
        :rtype: int
        """
        if self.dp:
            v = self.dp.popleft()
        else:
            v = -1
        if self.max_value_dp and  v == self.max_value_dp[0]:
            self.max_value_dp.popleft()
        return v

2.11 数学

2.11.1 圆圈中最后剩下的数字

# leetcode submit region begin(Prohibit modification and deletion)
class Solution(object):
    def lastRemaining(self, n, m):
        """
        :type n: int
        :type m: int
        :rtype: int
        """

        # 直接写超时(数组模拟超时)
        # nums = list(range(n))
        # cur = 0
        # print(m,n)
        # while len(nums) > 0:
        #     n_l = len(nums)
        #     print(n_l)
        #     if n_l == 1:
        #         return nums[0]
        #     # 例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
        #     # 0,1,2,3,4 cur = 0, m = 3, next = 2 = (0 + 3 - 1) % 4 = 2 --> 0,1,3,4
        #     # 0,1,3,4, cur = 2, m = 3, next = 0 = (2 + 3 - 1) % 4 = 0 --> 1,3,4
        #     # 1,3,4, cur = 0, next =
        #     # [0、1、3、4] cur_index = 2, m = 3, to_remove =
        #     to_remove_index =  (cur + m - 1) % n_l
        #     nums.remove(nums[to_remove_index])
        #     cur = to_remove_index


        # 数学法
        # 思路: 1.追踪最后留下的数字的index. 2.由n-1的index --> n的index 3. f(n) = (f(n - 1) + m) % n, 加m相当于是把数组移动回去,
        # %n, 是为了防止溢出
        if n < 1 or m < 1:
            return None
        cur = 0
        # 注意要定位到n
        for i in range(2, n + 1):
            cur = (cur + m) % i
        return cur

2.11.2 减绳子

1.当n == 2时,截成两段结果为11=1;当n == 3时,可得到最大结果为12 = 2;当n4时,为2*2=4;n5时,为23=6。
2.当 n > 5时,应剪出尽可能多的3,因为3
3 > 222,直至剩余长度小于5。因此,当n%3 == 0时,依次剪去3,直至最后一段为3,也就是剪成全是3的小段,此时结果为3 ** (n // 3);当n%31时,依次剪去3,直至最后一段为4,此时结果为3**((n-4) // 3) * 4;当n%32时,依次剪去3,直至最后一段为2,此时结果为3**((n-2) // 3) * 2。

class Solution:
    def cuttingRope(self, n: int) -> int:
        if n == 2:
            return 1
        if n == 3:
            return 2
        if n % 3 == 0:
            return (3 ** (n // 3)) % int(1e9+7)
        elif n % 3 == 1:
            return (3 ** ((n-4) // 3) * 4) % int(1e9+7)
        else:
            return (3 ** ((n-2) // 3) * 2) % int(1e9+7)

2.12 机智类题目

2.12.1 1+2+…+n和

# leetcode submit region begin(Prohibit modification and deletion)
class Solution(object):
    def __init__(self):
        self.res = 0

    def sumNums(self, n):
        """
        :type n: int
        :rtype: int
        """
        #  输入: n = 3, 1+2+3
        # 输出: 6
        #
        #
        #  示例 2:
        #
        #  输入: n = 9 1+2+3+4+5+6+7+8+9
        # 输出: 45

        # n > 1进入循环,否则不进入
        n > 1 and self.sumNums(n - 1)
        self.res += n
        return self.res
        

2.12.2

2.13 递归

2.13.1 数值的整数次方

class Solution(object):
    def myPow(self, x, n):
        """
        :type x: float
        :type n: int
        :rtype: float
        """
        # 直接这么搞会内存出问题
        # neg_sign = n < 0
        # n = abs(n)
        # s = 1
        # for i in range(n):
        #     s *= x
        # if neg_sign:
        #     return 1 / float(s)
        # else:
        #     return s

        # print(n)
        if n == 0:
            return 1
        if n < 0:
            return 1 / self.myPow(x, -n)
        if n % 2 == 0:
            return self.myPow(x*x, n // 2)
        else:
            return x * self.myPow(x, n - 1)

你可能感兴趣的:(算法,python)